react-mele
is a library which helps you decorate/alter React element trees, using a subset of CSS selectors, inspired by JQuery methods like addClass
, after
, append
, et cetera.
$ yarn add react-mele
$ npm install --save react-mele
Two reasons, as far as I can tell:
- Decorating children in wrapper components. For example, having a DropdownComponent which uses
props.children
to populate the component. You can usemele
then to easily add classNames, or icons, or whatever you want. - Theming. Suppose I have a form library which I want to style/theme. I can use a function which returns an element tree based on
props
which serves as the base theme.mele
would then allow you to customize this theme, adding elements, classes and properties where you need them. It also paves the way for some kind of plugin architecture.
Well, consider the following JSX:
let template = (
<ol>
<li>Item 1</li>
<li>Item 2</li>
</ol>
);
We can then add another list item, and add a className to the list items:
import mele from 'react-mele';
let modified =
mele(template)
.append('ol', <li>Item 3</li>)
.addClass('li', 'list-item')
.element();
Which will result in:
<ol>
<li class="list-item">Item 1</li>
<li class="list-item">Item 2</li>
<li class="list-item">Item 3</li>
</ol>
You can also wrap or replace the component:
modified = mele(template).replace('ol', ol => <div>{ol}</div>)
.. or set or remove properties
modified = mele(template).replace('ol', { reversed: 'reversed' });
The first thing you do, is import mele
:
import mele from 'react-mele';
You then call mele
with the element tree you want to modify. Mele then returns what we call a Themer
internally. After that, you can start chaining decorations:
let themer =
mele(<div/>)
.append('div', <span/>)
.after('span', <h1/>)
.addClass('span', 'foo');
mele
makes a copy of the element tree and adds the decorations specified. You can then get the result of the decorations by calling element
:
let decorated = themer.element();
The reason mele
decorates a copy, and not the element itself, is because the element tree is read-only. It might be changed later to just mutate (in production), but that'll be an implementation detail (which won't work in dev mode either). So: treat it like a copy.
Every command on a Themer
takes a selector
argument, which can be a string or a Function. The selector is resolved to a list of elements that meet the demands of the selector (meaning you can operate on a list of 1-n elements). The following string selectors are supported:
#id
.className
[attr=value]
tag
Additionally, mele supports type
selectors. Just pass in the React.Component class or stateless component.
Here's a list of commands you can use:
addClass
( selector:String|Function, className:string )
: adds a class to the element. You can use a space-separated list of classes.removeClass
( selector:String|Function, className:string )
: removes a class from the element. You can use a space-separated list of classes here as well.append
( selector:String|Function, child:Element )
: appends a child to the given element.prepend
( selector:String|Function, child:Element )
: prepends a child to the given element.after
( selector:String|Function, sibling:Element )
: adds a sibling after the given element.before
( selector:String|Function, sibling:Element )
: adds a sibling before the given element.replace
( selector:String|Function, replacementFn:Function )
: Calls the given function with the element, and uses the result to replace the element.prop
( selector:String|Function, properties:Object|Function )
: Copies to the given properties to the element. Ifproperties
is a function, it is called with the current properties of the element. The result is then used to replace the element's properties.removeProp
( selector:String|Function, propName:String )
: Removes the given property from the element. You can use a space-separated list of names here, and they will all be removed from the element's properties.
I don't really know. It's probably a bit brittle to start changing render instructions. But I'm not familiar enough with the React codebase to be able to determine how sensible this approach is. It is however fully tested though, so we'll see soon enough when/if it breaks.