Skip to content Skip to sidebar Skip to footer

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-rende

Solution 1:

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

varList = 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.

Solution 2:

IMO, React is still very new and there are very few established rules on how to work with data and reactive models like Backbone. This is also a strength, if you have an existing application - react can be integrated on some smaller parts of it without redefining the entire data flow.

I believe that since React can call render "smart" at any time – that is only re-rendering parts that have changed – you don’t really need to pass data as states. Just pass the data, add listeners on the top component and call forceUpdate when the model has changed and it will propagate down nicely.

It just seems more "right" to pass backbone models as props, not states.

One important thing that I learned the hard way is to use the model.cid as key (and not Math.random()) when rendering backbone model lists:

var listItems = this.props.myCollection.map(function(item){
    return<likey={item.cid}>{item.get("someAttr")}</li>;
});

Because otherwise React won’t be able to recognize what model to re-render because all of them will have new keys on each render.

Solution 3:

I had been playing around with the BackboneMixin mentioned here and a couple other react resources (of the limited info currently out there). I found that when I was listening to a collection that was being updated from the server, just as many n 'add' events are going to be triggered on the collection and listened to by the BackboneMixin, thus calling force update n number of times, which calls render and whatever is called from render n number of times.

Instead, I used underscore/lo-dash's throttle method to limit the number of times forceUpdate would be called. At the very least this has limited the render method from being called so much. I know react isn't actually doing any DOM manipulation there, and its just a virtual DOM, but still there is no reason it should be called 100 times for 100 immediate additions to a Collection.

So my solution looks like https://gist.github.com/ssorallen/7883081 but with the componentDidMount method like this instead:

componentDidMount: function() {
  //forceUpdate will be called at most once every secondthis._boundForceUpdate = _.throttle(this.forceUpdate.bind(this, null), 1000);
  this.getBackboneObject().on("all", this._boundForceUpdate, this);
}

Solution 4:

There's another BackboneMixin, courtesy of Eldar Djafarov, that re-renders your component when the model changes and also provides a very convenient way to get two-way databinding:

var BackboneMixin = {
    /* Forces an update when the underlying Backbone model instance has
     * changed. Users will have to implement getBackboneModels().
     * Also requires that React is loaded with addons.
     */
    __syncedModels: [],
    componentDidMount: function() {
      // Whenever there may be a change in the Backbone data, trigger a reconcile.this.getBackboneModels().forEach(this.injectModel, this);
    },
    componentWillUnmount: function() {
      // Ensure that we clean up any dangling references when the component is// destroyed.this.__syncedModels.forEach(function(model) {
        model.off(null, model.__updater, this);
      }, this);
    },
    injectModel: function(model){
      if(!~this.__syncedModels.indexOf(model)){
        var updater = this.forceUpdate.bind(this, null);
        model.__updater = updater;
        model.on('add change remove', updater, this);
      }
    },
    bindTo: function(model, key){
      /* Allows for two-way databinding for Backbone models.
       * Use by passing it as a 'valueLink' property, e.g.:
       *   valueLink={this.bindTo(model, attribute)} */return {
        value: model.get(key),
        requestChange: function(value){
            model.set(key, value);
        }.bind(this)
      };
    }
  }

Here's his jsFiddle that demonstrates the usage: http://jsfiddle.net/djkojb/qZf48/13/

Solution 5:

react.backbone seems to be the most recent solution for React-Backbone integration. Haven't tested it yet though.

Post a Comment for "Handling Backbone Model/collection Changes In React.js"