当使用React with Backbone时,我可以避免使用forceUpdate()吗?
Facebook React 鼓励你分离可变( state
)和不可变( props
)状态:
尽量保持尽可能多的组件无状态。 通过这样做,您可以将状态隔离为最合乎逻辑的位置,并最大限度地减less冗余,从而更容易推断您的应用程序。
当状态改变时,你应该调用setState
来触发虚拟DOM差异,只有在需要时才会导致真正的DOM更新。
有一种方法可以通过调用forceUpdate
手动触发DOM更新,但不鼓励 :
通常你应该尽量避免使用
forceUpdate()
,只能从render()
this.props
和this.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使得难以实现。