Search code examples
javascriptbackbone.jsreactjs

handling backbone model/collection changes in react.js


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?


Solution

  • 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.