React-Mutant
React-Mutant piggybacks your props to make nested updates easy in React. Instead of passing 'update' handlers into all components needing to update root component properties, React-Mutant can be used instead.
- Simpler solution than Flux, though not as robust. Ideal for smaller to mid-size applications or isolated components
- Inspired by Cortex http://https://github.com/mquan/cortex
- Built-in listener for root component/top-down updates
- Provide your own mixin methods to extend the React-Mutant instance
Example
JSFiddle Live Example: http://jsfiddle.net/69z2wepo/2975/
// Create Mutant Objects with default properties
var postMutant = new Mutant({
id: 0,
title: "",
content: ""
});
var commentsMutant = new Mutant({
comments: []
});
// Root-level component
var App = React.createClass({
// validate properties as your normally would
propTypes: {
post: React.PropTypes.shape({
id: React.PropTypes.number,
title: React.PropTypes.title,
content: React.PropTypes.content
}),
comments: React.PropTypes.shape({
comments: React.PropTypes.array
})
},
// set default props to Mutant objects
getDefaultProps: function () {
return {
post: postMutant,
comments: commentsMutant
}
},
// listen to 'update' events, pass in updated mutant and set root props to new mutant
componentWillMount: function () {
var self = this;
postMutant.mutant.on("update", function (newMutant) {
self.setProps({post: newMutant});
});
commentsMutant.mutant.on("update", function (newMutant) {
self.setProps({comments: newMutant});
});
},
// {...this.props.post} = mutant object we created for post
// {...this.props.comments} = mutant object we created for comments
render: function () {
return (
<div className="app">
<Post {...this.props.post} />
<Comments {...this.props.comments} />
</div>
);
}
});
var Post = React.createClass({
componentDidMount: function () {
// once mounted, use post.mutant.set to update props and trigger root-level 'update'
this.props.mutant.set({id: 1, title: "title", content: "content"});
},
render: function () {
return (
<div className="post">
<h1>{this.props.title}</h1>
<p>{this.props.content}</p>
</div>
);
}
});
var Comments = React.createClass({
add: function () {
var current = this.props.comments;
current.push({username: "me", date: "now", comment: "comment"});
this.props.mutant.set({comments: current});
},
remove: function (index, e) {
var current = this.props.comments;
current.splice(index, 1);
this.props.mutant.set({comments: current});
},
render: function () {
var i = -1;
var remove = this.remove;
var comments = this.props.comments || [];
var list = comments.map(function (comment) {
i++;
comment.index = i;
return <Comment key={i} {...comment} remove={remove} />
});
return (
<div className="comments">
<button onClick={this.add}>Add Comment</button>
{list}
</div>
);
}
});
var Comment = React.createClass({
propTypes: {
index: React.PropTypes.number,
username: React.PropTypes.string,
date: React.PropTypes.string,
comment: React.PropTypes.string
},
render: function () {
return (
<div className="comment">
<div>{this.props.index}</div>
<div>{this.props.username}</div>
<div>{this.props.date}</div>
<div>{this.props.comment}</div>
<button onClick={this.props.remove.bind(null, this.props.index)}>Remove</button>
</div>
);
}
});
React.render(<App />, document.getElementById("app"));
Cortex comparison
Feature | Mutant | Cortex |
---|---|---|
Access prop values | Standard | .getValue() |
Works with React PropType validation out of the box | Yes | No |
Array & Hash methods | No | Yes |
Update Root Component easily | Yes | Yes |
Callbacks | Yes | Yes |
Deeply Nested Updates | Not yet | Yes |
.getChanges() | .history() instead | Yes |
.didChange() | .set() returns false instead | Yes |
Batch updates | Not Yet | Yes |
.remove() | No | Yes |
NPM | Yes | Yes |
ES6 | No | Yes |
Bower | No | Yes |
CDN | Not Yet | No |
Key Differences
The three primary reasons for creating React-Mutant over Cortex are:
- No need to use .getValue() when accessing prop values
- Works with React.PropTypes validation out of the box
- Lighter objects by not including Hash/Array methods
Nested Updates
Also, though not currently supported, future releases would like to include deeply nested updates.
So instead of:
mymutant.mutant.set({root: {level1: {level2: {level3: "newvalue"}}}});
You'll be able to:
mymutant.mutant.root.level1.level2.level3.set("newvalue");