私はReact.jsを初めて使用し、アプリケーションにこのライブラリを使用するかどうかを決定するためにいくつかのコアコンセプトを理解するのに苦労しています。私の主な問題は、サーバーからフェッチしたモデルの更新を実際に処理することです。
5つの異なるモデルを表示するページがあるとします。この記事で説明した方法でビルドしました: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html なので、 「ルート」コンポーネントがあり、5つのモデルすべてが渡され、プロップを使用して、このモデルを保持するコンポーネントに降りていきます。したがって、2つのモデルが更新され(このイベントは、reactコンポーネントの外部にあるモデルコードから取得されます)、UIに反映する必要があります。これを行う最良の方法は何ですか?
私は次のオプションについて考えています:
よろしくお願いします。私の問題を明確に説明できたことを願っています。
同じコンポーネントを使用してrenderComponentを再度呼び出すことは、データが異なることは、component.setProps()を呼び出すことと同じです。したがって、すべてのモデルを最小公分母の状態として保持するか、変更されたときにもう一度setProps/renderComponentを呼び出すだけです。
現在、新しいデータをコンポーネントに渡す方法が少なくとも3つわかっています。
3番目のオプションについては、 StevenH の答えに触発され、少し拡張されました。 j sfiddle.net/kb3gN/12002/ で実装を確認してください。
var Data = { value: 1 };
var dataChange = function(callback){
if(callback){
callback(Data);
setInterval(function(){
Data.value++;
callback(Data);
}, 1000);
}
return Data;
};
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return {
data: this.props.dataChange()
};
},
componentDidMount: function() {
this.props.dataChange(this.updateHandler)
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value: <World data={this.state.data} />
</div>
);
}
});
React.renderComponent(<Hello dataChange={dataChange} />, document.body);
また、 jsfiddle.net/kb3gN/12007 に拡張バージョンがあります。
function ListenersService(){
var listeners = {};
this.addListener = function(callback){
var id;
if(typeof callback === 'function'){
id = Math.random().toString(36).slice(2);
listeners[id] = callback;
}
return id;
}
this.removeListener = function( id){
if(listeners[id]){
delete listeners[id];
return true;
}
return false;
}
this.notifyListeners = function(data){
for (var id in listeners) {
if(listeners.hasOwnProperty(id)){
listeners[id](data);
}
}
}
}
function DataService(ListenersService){
var Data = { value: 1 };
var self = this;
var listenersService = new ListenersService();
this.addListener = listenersService.addListener;
this.removeListener = listenersService.removeListener;
this.getData = function(){
return Data;
}
setInterval(function(){
Data.value++;
listenersService.notifyListeners(Data);
}, 1000);
}
var dataSevice = new DataService(ListenersService);
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
return {
data: this.props.dataService.getData()
};
},
componentDidMount: function() {
this.props.dataService.addListener(this.updateHandler)
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value: <World data={this.state.data} />
</div>
);
}
});
React.renderComponent(<Hello dataService={dataSevice} />, document.body);
この実装は、孤立したコンポーネント(HelloコンポーネントはDataService APIに依存しているため)のアイデアに完全には従いませんが、さらに抽象化することができ、コンポーネントが従うアプリ固有の規則はアプリ開発者次第です。たとえば、 jsfiddle.net/kb3gN/12015 (halloDataStaticオブジェクトとhalloDataDynamicコールバック)で最初と2番目の例の組み合わせを参照してください。
注:この例で使用されているListenersServiceはObserverパターンに従っており、パターン自体には多くのシナリオで長所よりも短所があります。しかし、それ以外に、これらの例で示したかったのは、コールバックを使用したデータバインディングの方法があることです。
<div id="static"></div>
<div id="dynamic"></div>
<script>
function ListenersService(){
var listeners = {};
this.addListener = function(callback){
var id;
if(typeof callback === 'function'){
id = Math.random().toString(36).slice(2);
listeners[id] = callback;
}
return id;
}
this.removeListener = function( id){
if(listeners[id]){
delete listeners[id];
return true;
}
return false;
}
this.notifyListeners = function(data){
for (var id in listeners) {
if(listeners.hasOwnProperty(id)){
listeners[id](data);
}
}
}
}
function DataService(ListenersService){
var Data = { value: 1 };
var self = this;
var listenersService = new ListenersService();
this.addListener = listenersService.addListener;
this.removeListener = listenersService.removeListener;
this.getData = function(){
return Data;
}
setInterval(function(){
Data.value++;
listenersService.notifyListeners(Data);
}, 100);
}
var dataSevice = new DataService(ListenersService);
var halloDataDynamic = function(callback){
var data = dataSevice.getData();
if(callback){
dataSevice.addListener(function(data){
callback(data);
});
}
return data;
};
var halloDataStatic = dataSevice.getData();
var World = React.createClass({
render: function() {
return <strong>{this.props.data.value}</strong>;
}
});
var Hello = React.createClass({
getInitialState: function() {
var data;
if(typeof this.props.halloData === 'function'){
data = this.props.halloData(this.updateHandler)
}
else data = this.props.halloData;
return {
data: data
};
},
updateHandler: function(data) {
this.setState({
data: data
});
},
render: function() {
return (
<div>
Value {this.props.name}: <World data={this.state.data} />
</div>
);
}
});
</script>
React.renderComponent(<Hello halloData={halloDataStatic} name="static"/>, document.getElementById('static'));
React.renderComponent(<Hello halloData={halloDataDynamic} name="dynamic"/>, document.getElementById('dynamic'));
データを小道具として子コンポーネントに渡す場合は、より高いレベルでデータを更新するだけで、同じプロパティオブジェクトを使用するすべてのコンポーネントにレンダリングが強制されます。この簡単な例を考えてみましょう:
var World = React.createClass({
render: function() {
return <strong>{this.props.name}</strong>;
}
});
var Hello = React.createClass({
clickHandler: function() {
this.setProps({ name: 'earth' });
},
render: function() {
return (
<div>
Hello <World name={this.props.name} />
<button onClick={this.clickHandler}>Click me</button>
</div>
);
}
});
これで、ユーザーがボタンをクリックすると、Hello
コンポーネントのプロパティが変更されますが、同じプロパティ(またはデータ)オブジェクトを子に渡したので、ユーザーはそれに反応して、それに応じてシャドウDOMを更新します。
これが私の意味のフィドルです: http://jsfiddle.net/xkCKR/
外部データオブジェクトがある場合は、それをトップコンポーネントに渡すだけです。ただし、これは双方向のバインディングがあるという意味ではありません。
// simple example of a data model
var Data = { name: 'world' };
var World = React.createClass({
render: function() {
return <strong>{this.props.data.name}</strong>;
}
});
var Hello = React.createClass({
clickHandler: function() {
this.setProps({
data: { name: 'earth' }
});
},
render: function() {
return (
<div>
Hello <World data={this.props.data} />
<button onClick={this.clickHandler}>Click me</button>
</div>
);
}
});
React.renderComponent(<Hello data={Data} />, document.body);
これは、reactがプロパティの一方向バインディングを使用するため機能します。ただし、子コンポーネントがそのプロパティを更新すると言った場合、その子コンポーネントは親にはなりません。そのためには、 ReactLinkアドオン が必要か、バックボーンが提供するようなpub/subインターフェースを使用します。