在过去的几周里,我一直在使用facebooks框架React.js和Backbone一起工作,我仍然不完全确定当一个React组件发生变化时,重新渲染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>;
}
不同的方法有哪些好处和缺点? 有没有办法做到这一点 The React方式?
答案 0 :(得分: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放在顶级组件上 - 任何后代都将同时自动重新呈现。
答案 1 :(得分:10)
IMO,React仍然很新,关于如何使用Backbone等数据和反应模型的规则很少。这也是一个优势,如果你有一个现有的应用程序 - 可以将反应集成在它的一些较小的部分而不重新定义整个数据流。
我相信,因为React可以随时调用渲染“智能” - 这只是重新渲染已经改变的部分 - 你真的不需要将数据作为状态传递。只需传递数据,在顶部组件上添加侦听器,并在模型发生变化时调用forceUpdate
,它将很好地传播。
将骨干模型作为道具而不是状态传递似乎更“正确”。
我学到很多困难的一件重要事情是在渲染骨干模型列表时使用model.cid
作为键(而不是Math.random()
):
var listItems = this.props.myCollection.map(function(item){
return <li key={item.cid}>{item.get("someAttr")}</li>;
});
因为否则React将无法识别要重新渲染的模型,因为所有模型都会在每个渲染上都有新的键。
答案 2 :(得分:5)
我一直在玩这里提到的BackboneMixin和其他一些反应资源(目前有限的信息)。我发现当我正在收听从服务器更新的集合时,就会在集合上触发多个n'add'事件并由BackboneMixin监听,因此调用force update n次,调用渲染和从渲染n次调用的任何东西。
相反,我使用了underscore / lo-dash的throttle方法来限制forceUpdate的调用次数。至少这限制了渲染方法被调用这么多。我知道react实际上并没有在那里进行任何DOM操作,它只是一个虚拟DOM,但仍然没有理由它应该被100次调用100次立即添加到Collection。
所以我的解决方案看起来像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);
}
答案 3 :(得分:1)
还有另一个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/
答案 4 :(得分:1)
react.backbone似乎是React-Backbone集成的最新解决方案。但是,Haven还没有测试过它。