【问题标题】:handling backbone model/collection changes in react.js处理 react.js 中的主干模型/集合更改
【发布时间】:2013-12-04 09:30:55
【问题描述】:

在过去的几周里,我一直在使用 facebooks 框架 React.js 和 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) });
};

我还看到过一些变体,其中 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>;
}

不同的方法有什么优点和缺点? 有没有一种方法是 React 方式

【问题讨论】:

    标签: javascript backbone.js reactjs


    【解决方案1】:

    您可以使用基于此 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 放在顶级组件上——任何后代都将同时自动重新渲染。

    【讨论】:

    • 请注意,对于 BackboneMixin 的链接实现,getBackboneModels 必须始终返回相同的模型列表。如果 getBackboneModels 更改为返回不同的对象,则事件处理程序将无法正确绑定和解除绑定。如果这对您来说是个问题,那么让 BackboneMixin 变得更智能一点也不难。
    • 由于主干模型事件被传递给包含集合,因此应稍微更改 Mixin 以侦听集合。我最终得到:componentDidMount: function() { this._boundForceUpdate = this.forceUpdate.bind(this, null); this.getBackboneObject().on("all", this._boundForceUpdate, this); },
    • Re: 只将它放在顶级组件中,如果在子组件中我需要对集​​合进行切片或创建它的另一个子集,我是否正确假设子集/切片集合是否需要另一个 mixin 实例化?谢谢。
    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 我喜欢这种方式比将集合添加到状态更简洁一些,但另一方面,如果有任何变化(例如集合)在状态内部,它会更清楚一些,因为它是您在渲染时感兴趣的集合的状态。对于哪种方式更可取,我仍然有点担心。
    【解决方案3】:

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

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

    【讨论】:

    • 这是我目前正在使用的。目前尚不完全清楚我将如何处理更精细的事件。但我想我可以依靠 listenTogetInitialState...
    【解决方案4】:

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

    【讨论】:

    • 有人能解释一下如何将模型添加到 __syncedModels 中吗?还是有错误但没有?
    • @Tom 检查小提琴,在您的自定义组件中,您应该定义 getBackboneModels 它将返回将进入 __syncedModels 的模型
    • 我明白应该发生什么,我只是无法理解 getBackboneModels 的返回值是如何添加到 mixin 中的 __syncedModels 中的:它必须缺少一行 - this.__syncedModels.push(model);对吗?
    • 即小提琴有效,其他简单用途也有效,但__syncedModels 守卫实际上并没有工作,所以如果getBackboneModels 多次返回同一个模型实例,forceUpdate 将被多次触发。
    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-30
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-06
      • 1970-01-01
      相关资源
      最近更新 更多