23

サーバー側から HTML をレンダリングする同形 JavaScript アプリケーションの POC を行っています。POC は単純な HTML で動作していますが、API 呼び出しを行い、JSON 応答を取得してレンダリング関数に送信したいと考えています。いろいろな方法を試しましたがうまくいきません。

私は何が欠けていますか?私はReact.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>
        )
      }

  });
4

3 に答える 3

5

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

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

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

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

しかし、これを行うための最も洗練された方法は、特に RxJS を使用して、react コンポーネントの非同期レンダリングを整理することです。

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

  1. views - ロジックのない React コンポーネント (単なるビュー)
  2. models - 現在の状態のオブザーバブル (初期データはスーパーエージェントを使用して読み込まれ、その後、他のモデルやアクションの結果と組み合わされます)。単純なケースでは、次のようになります。

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

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

    updatesSubject = new Rx.Subject();

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

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

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

于 2015-04-29T11:51:20.553 に答える
3

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

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

サーバーでデータを取得することを選択した場合は、最初に api-request ロジックをコンポーネントの外に移動します。次に、コールバックでコンポーネントをレンダリングし、fetched-data を として渡しますprop。次のようになります。

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

コンポーネント内では、 経由でデータにアクセスできますthis.props.categories

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

于 2015-04-29T08:47:58.903 に答える
0

superagentを使用する必要があります。私にとっては非常にうまく機能します。また、最も重要な部分が欠けています。flux を使用してサーバーからデータを取得する必要があります。flux は facebook が強く推奨する方法です。fluxアーキテクチャを使用するのは非常に簡単です。

于 2015-06-09T06:49:49.033 に答える