I've been working with facebooks framework React.js together with Backbone for the last couple of weeks and I'm still not entirely sure what is the most appropriate way to re-render a React component when there are changes in a Backbone collection that has been passed in as a prop.
currently what I do is in componenentWillMount
I set up change/add/remove
listeners on the collection and set state when it triggers:
componentWillMount: function(){
var myCollection = this.props.myCollection;
var updateState = function(){
this.setState({myCollection: myCollection.models});
}
myCollections.on("add remove", updateState, this);
updateState();
}
render: function(){
var listItems = this.state.myCollection.map(function(item){
return <li>{item.get("someAttr")}</li>;
});
return <ul>{listItems}</ul>;
}
I have seen examples where the models are cloned to the state:
var updateState = function () {
this.setState({ myCollection: _.clone(this.myCollection.models) });
};
I've also seen variants where model/collection in props is used directly in render instead of using state, and then forceUpdate is called when the collections/model changes, causing the component to re-render
componentWillMount: function(){
var myCollection = this.props.myCollection;
myCollections.on("add remove", this.forceUpdate, this);
}
render: function(){
var listItems = this.props.myCollection.map(function(item){
return <li>{item.get("someAttr")}</li>;
});
return <ul>{listItems}</ul>;
}
what benefits and drawbacks are there to the different approaches? Is there a way of doing it that is The React way?
Instead of manually binding event listeners, you can use a mixin based on this BackboneMixin to help automatically bind and unbind the listeners:
https://github.com/facebook/react/blob/1be9a9e/examples/todomvc-backbone/js/app.js#L148-L171
Then you simply write
var List = React.createClass({
mixins: [BackboneMixin],
getBackboneModels: function() {
return [this.props.myCollection];
},
render: function(){
var listItems = this.props.myCollection.map(function(item){
return <li>{item.get("someAttr")}</li>;
});
return <ul>{listItems}</ul>;
}
});
and the component will be rerendered when anything changes in the collection. You only need to put BackboneMixin on the top-level component -- any descendants will be rerendered automatically at the same time.