私は過去2週間、facebookフレームワーク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>;
}
異なるアプローチにはどのような利点と欠点がありますか?それを行う方法はありますかThe React way?
イベントリスナーを手動でバインドする代わりに、このBackboneMixinに基づくミックスインを使用して、リスナーを自動的にバインドおよびバインド解除できます。
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を最上位のコンポーネントに配置するだけで、すべての子孫が同時に自動的に再レンダリングされます。
IMO、Reactはまだ非常に新しく、データやバックボーンのような反応モデルを操作する方法について確立されたルールはほとんどありません。これは、既存のアプリケーションがある場合の強みでもあります-反応することができますデータフロー全体を再定義することなく、その一部に統合できます。
Reactはいつでもrender "smart"を呼び出すことができます。つまり、変更されたパーツを再レンダリングするだけなので、データを状態として渡す必要はありません。データ、一番上のコンポーネントにリスナーを追加し、モデルが変更されたときにforceUpdate
を呼び出します。モデルが適切に伝搬されます。
状態ではなく、小道具としてバックボーンモデルを渡すほうが「正しい」ように思われます。
私が難しい方法を学んだ重要なことの1つは、バックボーンモデルリストをレンダリングするときに、_model.cid
_をキーとして(Math.random()
ではなく)使用することです。
_var listItems = this.props.myCollection.map(function(item){
return <li key={item.cid}>{item.get("someAttr")}</li>;
});
_
それ以外の場合、Reactは、どのモデルを再レンダリングするかを認識できません。すべてのモデルで、各レンダリングで新しいキーが使用されるためです。
私はここで述べたBackboneMixinと他のいくつかの反応リソース(現在そこにある限られた情報のもの)をいじっていました。サーバーから更新されているコレクションをリッスンしているときに、コレクションでn個の「追加」イベントがトリガーされ、BackboneMixinによってリッスンされるため、force updateをn回呼び出すことがわかりました。これはrenderを呼び出し、何でもrenderからn回呼び出されます。
代わりに、アンダースコア/ローダッシュのスロットルメソッドを使用して、forceUpdateが呼び出される回数を制限しました。少なくともこれにより、renderメソッドがあまり呼び出されないように制限されています。 reactは実際にはそこでDOM操作を行わず、単なる仮想DOMですが、コレクションに100を追加するために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);
}
別のBackboneMixin 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/
react.backbone は、React-Backbone統合の最新のソリューションのようです。まだそれをテストしていません。