web-dev-qa-db-ja.com

反応jsから安らかなAPI呼び出しを行う

サーバー側からhtmlをレンダリングする同形javascriptアプリケーションのPOCを実行しています。 POCは単純なhtmlで動作していますが、api呼び出しを行い、json応答を取得してrender関数に送信したいです。いろいろ試してみましたが、うまくいきません。誰が私が行方不明になっているのか教えてください。

私はjsに反応することは非常に新しく、どんな助けも本当に感謝されます

loadCategoriesFromServer: function() {
        var self = this;

// get walking directions from central park to the empire state building
    var http = require("http");
    url = "api url here";
        var request = http.get(url, function (response) {
            // data is streamed in chunks from the server
            // so we have to handle the "data" event    
            var buffer = "", 
                data,
                route;

            response.on("data", function (chunk) {
                buffer += chunk;
            }); 

            response.on("end", function (err) {

              data = JSON.parse(buffer);

              //console.log(data.d);
              //console.log(data.d.Items);
                self.setState({
                    categories: data.d.Items
                });
            }); 
        });
    }, // load from server end

    getInitialState: function() {
        return { categories: [] };
    },

    componentWillMount: function() {
        console.log("calling load categories")
        this.loadCategoriesFromServer();
    },
render: function () {

        //console.log("data");
        //console.log(this.state.categories);

        var postNodes = this.state.categories.map(function (cat) {
          console.log(cat);
        });

        return (
          <div id="table-area">
             //i want to Paint the data here..
          </div>
        )
      }

  });
22
kobe

サーバー側をレンダリングする必要がある場合に備えて、componentWillMountを使用してコンポーネントの内部をフェッチするのは適切な場所ではありません。あなたはそれを何らかの形でコンポーネントから移動し、実際のデータを取得した後に小道具として渡す必要があります-例えば、@ JakeSendarが彼の答えで示唆したように。

私はReactで同形アプリをやった経験があり、私が直面した主な問題は、最初のレンダリングの前にすべてのデータがロードされるまで待つ方法です

@FakeRainBrigandが既にコメントで言及しているように、これを行う方法は1つだけではなく、要件によって異なります。

同形アプリを作成する方法はいくつかありますが、私の観点から興味深いのは https://github.com/webpack/react-starter および http://fluxible.io/

しかし、これを行う最もエレガントな方法は、私が自分で考えたように、特にRxJSを使用して、reactコンポーネントの非同期レンダリングを整理することです。

一般に、私のアプリケーションは次のように構成されています。

  1. ビュー-Reactロジックのないコンポーネント(ビューのみ)
  2. モデル-現在の状態のオブザーバブル(初期データはスーパーエージェントを使用してロードされ、他のモデルやアクションの結果と結合されます)。簡単な場合、次のようなものです。

    Rx.Observable.defer(fetchData).concat(updatesSubject).shareReplay()

  3. アクション(またはインテント)-ユーザー入力を収集し、何かを実行し、アクション結果をサブスクライバーモデルやその他のアクションにディスパッチするために使用されるオブザーバー。単純な場合には次のようなものです:

    updatesSubject = new Rx.Subject();

    action = new Rx.Subject();action.switchMap(asyncRequest).subscribe(updatesSubject)

  4. components-モデル、他のコンポーネント、アクションから結合されたObservables(仮想DOM要素のストリーム)( Observable React要素を作成する方法と理由を説明するメモRxJS )で、部分的なコンポーネントを追加することも計画しています(タプル:反応するコンポーネント、オブザーバブル、オブザーバー、プロパティ。DIを使用して部分的に塗りつぶされています)

  5. ルーター-場所の変更を処理するコンポーネント。一般的な主な機能は、場所の変更を仮想DOM要素とメタ情報のストリームにマッピングすることです。しかし、詳細には、私の場合は少し複雑です(URL生成、アクティブなURL強調表示、ナビゲーション時のスクロールの処理、またネストされたルートと複数のビューの可能性があります)

これはすべて、DIコンテナーを使用して一緒に組み立てられます。私の場合、 angular2 DIコンテナー に似ていますが、特定のニーズに合わせて非常に簡略化されています。

コンポーネント、モデル、およびアクションは、DIを使用して作成されます。

サーバー側のアプリケーションは次のようになります。

var rootInjector = new Injector();
// setup server specific providers
rootInjector.provide(..., ...)

app.get('/*', function(req,res){
    var injector = rootInjector.createChild();
    // setup request specific providers
    injector.provide(..., ...);

    injector.get(Router)
       .first()
       .subscribe(function(routingResult){ 
          res.render('app', {
              title: routingResult.title,
              content: React.renderToString(routingResult.content)
          });
       });
}

クライアント側でも同様:

var rootInjector = new Injector();
// setup server specific providers
// actually this is omitted in my case because default providers are client side
rootInjector.provide(..., ...)
contentElement = document.getElementById('#content');

rootInjector.get(Router)
   .subscribe(function(routingResult){ 
      document.title = routingResult.title;
      React.render(routingResult.content, contentElement)
   });

フラックスと比較して、アプリを整理するためのより宣言的で強力な方法です。そして、同型アプリの場合-私にとっては、さまざまなハックが流動的であることがはるかに良く見えます。しかし、もちろん欠点もあります...-より複雑です。

おそらく後日、私はこれらすべてをオープンソース化しますが、今のところは、公開する準備ができていません。

UPD1:

元の答えは少し時代遅れであり(後で更新する予定です)、この分野でいくつかの進歩があります。

すでにオープンソース化されている上記のコードへのリンク:

  • DIコンテナ: di1
  • 反応コンポーネントのコンテナー(オブザーバブルとオブザーバーへのビューの接続): rx-react-container
  • RxJSとReactを使用して同形ウィジェットを実装するためのスターターテンプレート、および上記のライブラリ: Reactive Widgets

完全なアプリケーションについて(作業はまだ進行中で、ドキュメントはあまり良くありませんが、一般的には明確なはずです):

  • アイソモフィックリアクティブアプリケーション用に特に構築されたルーター router1 およびそれを使用するためにコンポーネントを反応させる router1-react
  • 上記のルーターとすべてのライブラリを備えたアプリケーションテンプレート: router1-app-template
5
Bogdan Savluk

ReactのrenderToStringメソッド(サーバー上のコンポーネントのレンダリング用)は同期的です。したがって、APIリクエストなどのあらゆる種類の非同期タスクは、コンポーネントがレンダリングされるまでに保留されます。

サーバーまたはクライアントでデータを取得するかどうかに応じて、これを修正する方法がいくつかあります。

サーバー上のデータを取得することを選択した場合、最初にAPIリクエストロジックをコンポーネントの外部に移動します。次に、コンポーネントをコールバックでレンダリングし、フェッチしたデータをpropとして渡します。次のようになります。

response.on("end", function (err) {
  var data = JSON.parse(buffer);
  var markup = React.renderToString(Component({categories: data}));
});

コンポーネント内では、this.props.categoriesを介してデータにアクセスできます。

もう1つのオプションは、クライアントでAPI要求を処理することです。 componentDidMountでAJAXリクエストを作成し、フェッチしたデータからコンポーネントの状態を設定します。要求ロジックは、componentDidMount(非同期ではなく、サーバーで呼び出されます)ではなく、componentWillMount(非同期で、クライアントで呼び出されます)に存在します。

3
Jake Sendar

superagent を使用する必要があります。私にとっては非常にうまく機能します。また、最も重要な部分が欠落しているため、fluxを使用してサーバーからデータをフェッチし、フラックスは、facebookが強く推奨する方法であり、非常に使いやすい flux architecture です。