6

React ベースの SPA をサーバー側でレンダリングしたいと思います (最近はそうではありません)。したがって、React をreact-routerredux 、およびisomorphic starterkitのようなビルド レイヤーと組み合わせたいと考えています。

すべてを結合するhapi Universal reduxがありますが、フローを整理する方法に苦労しています。私のデータは、REST API の複数のエンドポイントから来ています。コンポーネントが異なればデータのニーズも異なり、クライアントにジャスト イン タイムでデータをロードする必要があります。サーバーでは、特定のルート (コンポーネントのセット) のすべてのデータを取得し、必要なコンポーネントを文字列にレンダリングする必要があります。

私の最初のアプローチでは、redux のミドルウェアを使用して非同期アクションを作成しました。これは、データをロードし、promise を返し、SOME_DATA_ARRIVEDpromise が解決されたときにアクションをトリガーします。次に、レデューサーがストアを更新し、コンポーネントが再レンダリングされます。すべて問題ありません。原則として、これは機能します。しかし、ルーティングが始まると、流れがぎこちなくなることに気付きました。

多数のデータ レコードを一覧表示する一部のコンポーネントには、レコードをフィルター処理するための複数のリンクがあります。フィルタリングされたすべてのデータ セットは、 のような独自の URL を介して利用できる必要があります/filter-by/:filter。そのため、さまざまなコンポーネントを使用<Link to={...}>して、クリック時に URL を変更し、ルーターをトリガーします。ルーターは、現在の URL によって表される状態に従ってストアを更新する必要があります。これにより、関連するコンポーネントが再レンダリングされます。

それを達成するのは容易ではありません。最初にアクションをトリガーしようとcomponentWillUpdateしました。これにより、データが非同期に読み込まれ、ストアにデータが入力され、コンポーネントの再レンダリング サイクルがもう 1 回発生しました。ただし、これはサーバーでは機能しません。3 つのライフサイクル メソッドしかサポートされていないためです。

だから私はこれを整理する正しい方法を探しています。ユーザーの観点からアプリの状態を変更するアプリとのユーザー インタラクションは、URL を更新する必要があります。IMOこれにより、ルーターは何らかの方法で必要なデータをロードし、ストアを更新し、調整プロセスを開始する必要があります。

だからinteraction -> URL change -> data fetching -> store update -> re-render

initial state要求された URL からロードするデータを決定し、その状態をstore生成して reduxの生成に渡すことができるため、このアプローチはサーバーでも機能するはずです。しかし、私はそれを適切に行う方法を見つけていません。したがって、私には次のような疑問が生じます。

  1. まだ理解していない/知らないことがあるため、私のアプローチは間違っていますか?
  2. redux の REST API からロードされたデータを保持するのは正しいstoreですか?
  3. stateコンポーネントをredux に保持し、store他のコンポーネントを自分で管理するのは少し厄介ではありstateませんか?
  4. 持っているという考えはinteraction -> URL change -> data fetching -> store update -> re-render単に間違っていますか?

私はあらゆる種類の提案を受け付けています。

4

1 に答える 1

4

今日はまったく同じものをセットアップしました。私たちがすでに持っていたのは、react-router と redux でした。いくつかのモジュールをモジュール化して、それらに何かを注入しました – そしてビオラ – それは機能します。https://github.com/erikras/react-redux-universal-hot-exampleを参考にしました。

パーツ:

1.ルーター.js

function (location, history, store)promises を使用してルーターをセットアップするためにa を返します。routesすべてのコンポーネントを含む react-router のルート定義です。

module.exports = function (location, history, store) {
    return new Bluebird((resolve, reject)  => {
        Router.run(routes, location, (Handler, state) => {
            const HandlerConnected = connect(_.identity)(Handler);
            const component = (
                <Provider store={store}>
                    {() => <HandlerConnected />}
                </Provider>
            );
            resolve(component);
        }).catch(console.error.bind(console));
    });
};

2.store.js

初期状態を に渡すだけcreateStore(reducer, initialState)です。サーバーとクライアントでこれを行うだけです。クライアントの場合は、スクリプト タグ (つまり ) を介して状態を利用できるようにする必要がありますwindow.__initialstate__。詳細については、 http://rackt.github.io/redux/docs/recipes/ServerRendering.htmlを参照してください。

3. サーバー上でのレンダリング

データを取得し、そのデータで初期状態を設定します ( ...data)。createRouterrouter.js上から。res.render次のように jade テンプレートを高速レンダリングします

script.
    window.csvistate.__initialstate__=!{initialState ? JSON.stringify(initialState) : 'null'};
...
#react-start
    != html

var initialState = { ...data };
var store = createStore(reducer, initialState);
createRouter(req.url, null, store).then(function (component) {
    var html = React.renderToString(component);
    res.render('community/neighbourhood', { html: html, initialState: initialState });
});

4. クライアントの適応

その後、クライアントは基本的に同じことを行うことができます。React-Routerからのものでlocationある可能性がありますHistoryLocation

const initialState = window.csvistate.__initialstate__;
const store = require('./store')(initialState);

router(location, null, store).then(component => {
    React.render(component, document.getElementsByClassName('jsx-community-bulletinboard')[0]);
});

質問に答えるには:

  1. あなたのアプローチは正しいようです。私たちも同じです。状態の一部として URL を含めることもできます。
  2. redux ストア内の状態はすべて良好です。このようにして、信頼できる唯一の情報源を手に入れることができます。
  3. 私たちは今、何をどこに置くべきかをまだ検討中です。現在、サーバー上のデータを要求してcomponentDidMountいますが、それは既にそこにあるはずです。
于 2015-09-01T13:40:49.677 に答える