Facebook React encourages您要将可变(state
)和不可变(props
)状态分开:
尽量保持尽可能多的组件无状态。通过这样做,您可以将状态隔离到最合理的位置并最大限度地减少冗余,从而更容易推断您的应用程序。
当状态发生变化时,你应该调用setState
来触发虚拟DOM差异,这只会在需要时才会导致真正的DOM更新。
是一种通过调用forceUpdate
手动触发DOM更新的方法,但它是discouraged:
通常您应该尽量避免使用
forceUpdate()
,并且只能在this.props
中的this.state
和render()
中阅读。这使您的应用程序更加简单和高效。
但是,我看到的所有React + Backbone示例忽略此建议并在props
中存储模型和集合并调用forceUpdate
:
即使React自己的例子也使用forceUpdate
:
是否有更好的方法,它会带来什么好处?
答案 0 :(得分:18)
皮特的回答很棒。
骨干模型本质上是变异的,虽然(虽然本身不是问题)意味着在重新渲染时,你不会将旧版本的模型进行比较。这使得通过在组件的关键位置定义shouldComponentUpdate
方法来进行智能优化变得更加困难。 (由于其他原因,例如implementing undo,您也失去了轻松存储旧版本模型的能力。)
调用forceUpdate
只会跳过shouldComponentUpdate
并强制组件重新呈现。请注意,调用render
通常很便宜,如果render
的输出发生变化,React仍然只会触及DOM,因此这里的性能问题并不常见。但是,如果你可以选择使用不可变数据(包括传递来自toJSON()
的原始模型属性对象,如Pete建议的那样),我强烈推荐它。
答案 1 :(得分:17)
在有更好的答案之前,让我quote Pete Hunt,一个核心的React开发者:
Backbone模型的巨大成功是它为您管理数据流。当您致电
set()
时,它会通知您的应用数据已更改。 使用React,你会发现这不是必需的,因为您需要做的就是通过回调通知拥有该状态的组件,React确保所有孩子都是最新的。所以这个骨干的一部分是不太有用的IMO(并且人们倾向于以这种方式使用骨干而无论如何)。您不必传递纯JSON(虽然这是我倾向于做的,它适用于简单的数据模型),但如果您保持对象不可变,您将看到很多优点。
你可以通过在骨干模型上调用
toJSON()
并看看你喜欢它与传递模型来尝试这一点。
(强调我的)
有趣的是,Backbone.React.Component是我发现使用toJSON
的唯一示例,但出于某种原因,还使用setProps
代替setState
(discouraged too )。
我根据Pete Hunt的方法制作了一个简单的mixin(没有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;
});
它并不关心您拥有什么样的模型或系列。
惯例是 Backbone模型进入props
并且他们的JSON由mixin自动放入state
。您需要覆盖getBackboneState(props)
才能使其生效,并可选择watchBackboneProps
告诉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 +。
答案 2 :(得分:8)
我是Backbone.React.Component背后的开发人员。我们使用setProps的原因是因为它只是由组件所有者(最大的父级)调用。我看待它的方式,道具最好用于反应性更新(并传递给子组件)而不是状态,但如果你能指出一些原因为什么状态更好,我会很乐意开始朝着这种变化发展。 / p>
例如,有时我会将组件委托给其他人,其中transferPropsTo非常方便。使用状态使得实现更难。