32

在过去的几周里,我一直在使用 facebooks 框架 React.js 和 Backbone,但我仍然不完全确定当 Backbone 集合发生变化时重新渲染 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) });
};

我还看到了一些变体,其中道具中的模型/集合直接在渲染中使用而不是使用状态,然后在集合/模型更改时调用 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>;
}

不同方法有哪些优点和缺点?有没有一种方法是React 方式

4

5 回答 5

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 放在顶级组件上——任何后代都将同时自动重新渲染。

于 2013-12-06T08:59:47.047 回答
10

IMO,React 仍然很新,关于如何使用数据和响应式模型(如 Backbone)的既定规则很少。这也是一个优势,如果您有一个现有的应用程序 - react 可以集成在它的一些较小的部分上,而无需重新定义整个数据流。

我相信,由于 React 可以随时调用“智能”渲染——这只是重新渲染已更改的部分——你实际上不需要将数据作为状态传递。只需传递数据,在顶部组件上添加侦听器,并forceUpdate在模型更改时调用,它会很好地向下传播。

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

我以艰难的方式学到的一件重要的事情是在渲染主干模型列表时使用model.cidas 键(而不是):Math.random()

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

因为否则 React 将无法识别要重新渲染的模型,因为它们在每次渲染时都会有新的键。

于 2013-12-04T12:24:07.153 回答
5

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

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

所以我的解决方案看起来像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);
}
于 2014-01-28T16:01:19.197 回答
1

还有另一个 BackboneMixin,由 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/

于 2014-01-08T16:46:01.490 回答
1

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

于 2015-04-28T19:40:21.127 回答