当使用React with Backbone时,我可以避免使用forceUpdate()吗?

Facebook React 鼓励你分离可变( state )和不可变( props )状态:

尽量保持尽可能多的组件无状态。 通过这样做,您可以将状态隔离为最合乎逻辑的位置,并最大限度地减less冗余,从而更容易推断您的应用程序。

当状态改变时,你应该调用setState来触发虚拟DOM差异,只有在需要时才会导致真正的DOM更新。

有一种方法可以通过调用forceUpdate手动触发DOM更新,但不鼓励 :

通常你应该尽量避免使用forceUpdate() ,只能从render() this.propsthis.state中读取。 这使得您的应用程序更简单,更高效。

然而,我所看到的所有React + Backbone示例都忽略了这个build议 ,并且在props和调用forceUpdate存储模型和集合中forceUpdate

  • http://www.thomasboyt.com/2013/12/17/using-reactjs-as-a-backbone-view.html
  • usepropeller/react.backbone/blob/master/react.backbone.html
  • https://gist.github.com/ssorallen/7883081
  • http://eldar.djafarov.com/2013/11/reactjs-mixing-with-backbone/

即使React自己的例子使用forceUpdate

  • https://github.com/facebook/react/blob/master/examples/todomvc-backbone/js/app.js#L148

有没有更好的方法,它会带来什么好处?

皮特的回答非常好

主干模型本质上是可变的,而这本身并不是一个问题,意味着在重新渲染的时候,你将不会有旧版本的模型进行比较。 通过在组件的关键位置定义shouldComponentUpdate方法,这使得难以进行智能优化。 (由于其他原因,您也可以轻松存储旧版本的模型,比如实现撤消) 。

调用forceUpdate只会跳过shouldComponentUpdate并强制组件重新渲染。 请注意,调用render通常很便宜,如果render的输出已经改变,React仍然只会触摸DOM,所以这里的性能问题并不常见。 但是,如果你可以select使用不可变的数据(包括像petebuild议的那样从toJSON()传递原始模型属性对象),我会强烈推荐它。

在有更好的答案之前,让我引用核心React开发人员Pete Hunt的话 :

Backbone模式的巨大胜利是为您pipe理您的数据stream。 当你调用set()它会通知你的应用数据改变了。 有了React,你会发现这样做不是那么必要,因为你需要做的就是通过callback来通知拥有状态的组件,React可以确保所有的孩子都是最新的。 所以这部分主干是不太有用的IMO(而且人们倾向于用React这种方式来使用主干)。

你不必传递纯粹的JSON(虽然这是我倾向于做的,它适用于简单的数据模型),但是如果你保持你的对象不变,你会看到很多优点。

你可以在你的骨干模型上调用toJSON() ,看看你喜欢它,还是传递模型来试试。

(重点是我的)

有趣的是, Backbone.React.Component是我发现使用toJSON的唯一例子,但由于某种原因,也使用setProps而不是setState (这也不鼓励 )。

更新

我根据Pete Hunt的方法做了一个简单的混合(没有setProps ,没有forceUpdate ):

 define(function () { 'use strict'; var Backbone = require('backbone'), _ = require('underscore'); var BackboneStateMixin = { getInitialState: function () { return this.getBackboneState(this.props); }, componentDidMount: function () { if (!_.isFunction(this.getBackboneState)) { throw new Error('You must provide getBackboneState(props).'); } this._bindBackboneEvents(this.props); }, componentWillReceiveProps: function (newProps) { this._unbindBackboneEvents(); this._bindBackboneEvents(newProps); }, componentWillUnmount: function () { this._unbindBackboneEvents(); }, _updateBackboneState: function () { var state = this.getBackboneState(this.props); this.setState(state); }, _bindBackboneEvents: function (props) { if (!_.isFunction(this.watchBackboneProps)) { return; } if (this._backboneListener) { throw new Error('Listener already exists.'); } if (!props) { throw new Error('Passed props are empty'); } var listener = _.extend({}, Backbone.Events), listenTo = _.partial(listener.listenTo.bind(listener), _, _, this._updateBackboneState); this.watchBackboneProps(props, listenTo); this._backboneListener = listener; }, _unbindBackboneEvents: function () { if (!_.isFunction(this.watchBackboneProps)) { return; } if (!this._backboneListener) { throw new Error('Listener does not exist.'); } this._backboneListener.stopListening(); delete this._backboneListener; } }; return BackboneStateMixin; }); 

它不关心你有什么样的模型或集合。

约定是骨干模型进入props ,他们的JSON是通过混入state自动放置的 。 您需要重写getBackboneState(props)才能使其工作,并且可以selectwatchBackboneProps来告诉mixin何时使用新值调用setState

用法示例:

 var InfoWidget = React.createClass({ mixins: [BackboneStateMixin, PopoverMixin], propTypes: { stampModel: React.PropTypes.instanceOf(Stamp).isRequired }, // Override getBackboneState to tell the mixin // HOW to transform Backbone props into JSON state getBackboneState: function (props) { var stampModel = props.stampModel, primaryZineModel = stampModel.getPrimaryZine(); return { stamp: stampModel.toJSON(), toggleIsLiked: stampModel.toggleIsLiked.bind(stampModel), primaryZine: primaryZineModel && primaryZineModel.toJSON() }; }, // Optionally override watchBackboneProps to tell the mixin // WHEN to transform Backbone props into JSON state watchBackboneProps: function (props, listenTo) { listenTo(props.stampModel, 'change:unauth_like_count change:is_liked'); listenTo(props.stampModel.get('zines'), 'all'); }, render: function () { // You can use vanilla JSON values of this.state.stamp, // this.state.toggleIsLiked and this.state.primaryZine // or whatever you return from getBackboneState // without worrying they may point to old values } } 

注意:mixin需要Underscore 1.6.0+。

我是Backbone.React.Component的开发者。 我们使用setProps的原因是因为这只是由组件所有者(最大的父母)调用。 就我看来,道具比状态更适合用于react native更新(并传递给子组件),但如果你能指出为什么状态更好的一些原因,我会很乐意开始朝着这种改变发展。

例如,有时候我已经有组件委托给其他组件,transferPropsTo非常方便。 使用状态types使得难以实现。