处理react.js中的主干模型/集合更改

时间:2013-12-04 09:30:55

标签: javascript backbone.js reactjs

在过去的几周里,我一直在使用facebooks框架React.js和Backbone一起工作,我仍然不完全确定当一个React组件发生变化时,重新渲染React组件的最合适的方法是什么。已作为道具传入的骨干集合。

目前我所做的是componenentWillMount我在集合上设置了change/add/remove个侦听器,并在触发时设置了状态:

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>;
}

我见过将模型克隆到州的例子:

var updateState = function () {
    this.setState({ myCollection: _.clone(this.myCollection.models) });
};

我也看到过变体,其中props中的模型/集合直接用于渲染而不是使用状态,然后在集合/模型更改时调用forceUpdate,导致组件重新渲染

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>;
}

不同的方法有哪些好处和缺点? 有没有办法做到这一点 The React方式

5 个答案:

答案 0 :(得分:12)

您可以使用基于此BackboneMixin的mixin来帮助自动绑定和取消绑定侦听器,而不是手动绑定事件侦听器:

https://github.com/facebook/react/blob/1be9a9e/examples/todomvc-backbone/js/app.js#L148-L171

然后你只需写

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>;
    }
});

当集合中发生任何变化时,组件将被重新呈现。您只需要将BackboneMixin放在顶级组件上 - 任何后代都将同时自动重新呈现。

答案 1 :(得分:10)

IMO,React仍然很新,关于如何使用Backbone等数据和反应模型的规则很少。这也是一个优势,如果你有一个现有的应用程序 - 可以将反应集成在它的一些较小的部分而不重新定义整个数据流。

我相信,因为React可以随时调用渲染“智能” - 这只是重新渲染已经改变的部分 - 你真的不需要将数据作为状态传递。只需传递数据,在顶部组件上添加侦听器,并在模型发生变化时调用forceUpdate,它将很好地传播。

将骨干模型作为道具而不是状态传递似乎更“正确”。

我学到很多困难的一件重要事情是在渲染骨干模型列表时使用model.cid作为键(而不是Math.random()):

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

因为否则React将无法识别要重新渲染的模型,因为所有模型都会在每个渲染上都有新的键。

答案 2 :(得分:5)

我一直在玩这里提到的BackboneMixin和其他一些反应资源(目前有限的信息)。我发现当我正在收听从服务器更新的集合时,就会在集合上触发多个n'add'事件并由BackboneMixin监听,因此调用force update n次,调用渲染和从渲染n次调用的任何东西。

相反,我使用了underscore / lo-dash的throttle方法来限制forceUpdate的调用次数。至少这限制了渲染方法被调用这么多。我知道react实际上并没有在那里进行任何DOM操作,它只是一个虚拟DOM,但仍然没有理由它应该被100次调用100次立即添加到Collection。

所以我的解决方案看起来像https://gist.github.com/ssorallen/7883081但是使用了像这样的componentDidMount方法:

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

答案 3 :(得分:1)

还有另一个BackboneMixin courtesy of Eldar Djafarov,它在模型更改时重新渲染组件,并提供了一种非常方便的方式来获取双向数据绑定:

  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)
      };
    }
  }

这是他演示用法的jsFiddle: http://jsfiddle.net/djkojb/qZf48/13/

答案 4 :(得分:1)

react.backbone似乎是React-Backbone集成的最新解决方案。但是,Haven还没有测试过它。