web-dev-qa-db-ja.com

新しいサーバーデータをreact.jsコンポーネントに渡す

私はReact.jsを初めて使用し、アプリケーションにこのライブラリを使用するかどうかを決定するためにいくつかのコアコンセプトを理解するのに苦労しています。私の主な問題は、サーバーからフェッチしたモデルの更新を実際に処理することです。

5つの異なるモデルを表示するページがあるとします。この記事で説明した方法でビルドしました: http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html なので、 「ルート」コンポーネントがあり、5つのモデルすべてが渡され、プロップを使用して、このモデルを保持するコンポーネントに降りていきます。したがって、2つのモデルが更新され(このイベントは、reactコンポーネントの外部にあるモデルコードから取得されます)、UIに反映する必要があります。これを行う最良の方法は何ですか?

私は次のオプションについて考えています:

  1. 新しいデータを使用してrenderComponentをもう一度実行し、DOMのdiff反応テクニックを利用します。データの小さな変更に対してこれを行う必要があるので、私はこれについて心配しています。
  2. このモデルを保持するコンポーネントのsetStateを呼び出します。このようにして、データは小道具ではなく、(私が理解していることから)良い状態ではない状態になります。さらに、ルートコンポーネントの外にある子コンポーネントへの参照を取得する方法はありません。
  3. 複数のrenderComponent呼び出しがあるため、このようにして、このコンポーネントのsetPropsにアクセスできます。しかし、私は(ページ上ですべてのコンテナーを使用できるようにするために)いくつかのテンプレート作成作業を行う必要があり、すべての反応のアイデアを殺します。
  4. アプリケーションで可能なすべてのモデルを含む1つのルートコンポーネントをユーザーに表示し、モデルを変更するためにsetPropsを呼び出します。ここでの私の懸念は、このコンポーネントがかなり大きくなり、ある時点で「スパゲッティ」になることと、ポイント1からの懸念です。

よろしくお願いします。私の問題を明確に説明できたことを願っています。

23
Sergey Shvets

同じコンポーネントを使用してrenderComponentを再度呼び出すことは、データが異なることは、component.setProps()を呼び出すことと同じです。したがって、すべてのモデルを最小公分母の状態として保持するか、変更されたときにもう一度setProps/renderComponentを呼び出すだけです。

6
krs

現在、新しいデータをコンポーネントに渡す方法が少なくとも3つわかっています。

  1. コンポーネントを再レンダリングします。 Reactはこれを非常にうまく処理しているように見えるため、このメソッドの効率について心配する必要はありません。これに関する素晴らしい記事があります: JavaScriptフレームワークでの変更とその検出 および React.render による更新
  2. PubSubを使用して、コンポーネントにデータの変更が通知されるようにします( Reactコンポーネント間の通信方法 投稿で見つけることができるいくつかの役立つ例))。
  3. コールバックとのバインド(以下の3つのjsfiddlesを参照)

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'));
5
Bulki S Maslom

データを小道具として子コンポーネントに渡す場合は、より高いレベルでデータを更新するだけで、同じプロパティオブジェクトを使用するすべてのコンポーネントにレンダリングが強制されます。この簡単な例を考えてみましょう:

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インターフェースを使用します。

5
David Hellsing