2

Redux と React Router を使用するユニバーサル React アプリがあります。私のルートの一部には、クライアント上で AJAX リクエストをトリガーして表示用のデータをハイドレートするパラメーターが含まれています。サーバーでは、これらのリクエストを同期的に処理し、最初のリクエストでレンダリングできます。

私が直面している問題は次のとおりです。componentWillMountルーティングされたコンポーネントでライフサイクル メソッド (例: ) が呼び出されるまでに、最初のレンダリングに反映される Redux アクションをディスパッチするには遅すぎます。

これは、サーバー側のレンダリング コードの簡略図です。

ルート.js

export default getRoutes (store) {
  return (
    <Route path='/' component={App}>
      <Route path='foo' component={FooLayout}>
        <Route path='view/:id' component={FooViewContainer} />
      </Route>
    </Route>
  )
}

サーバー.js

let store = configureStore()
let routes = getRoutes()
let history = createMemoryHistory(req.path)
let location = req.originalUrl
match({ history, routes, location }, (err, redirectLocation, renderProps) => {
  if (redirectLocation) {
    // redirect
  } else if (err) {
    // 500
  } else if (!renderProps) {
    // 404
  } else {
    let bodyMarkup = ReactDOMServer.renderToString(
      <Provider store={store}>
        <RouterContext {...renderProps} />
      </Provider>)
    res.status(200).send('<!DOCTYPE html>' +
      ReactDOMServer.renderToStaticMarkup(<Html body={bodyMarkup} />))
  }
})

コンポーネントがサーバー上にFooViewContainer構築されると、最初のレンダリングの小道具はすでに修正されています。ストアにディスパッチするアクションは、 への最初の呼び出しにはrender()反映されません。つまり、ページ リクエストで配信されるものには反映されません。

idReact Router が渡すパラメーターは、それ自体では最初のレンダリングには役立ちません。その値を適切なオブジェクトに同期的にハイドレートする必要があります。この水分補給はどこに置けばいいですか?

render()1 つの解決策は、サーバー上で呼び出される場合に、メソッド内にインラインで配置することです。1) 意味的に意味がなく、2) 収集したデータがストアに適切にディスパッチされないため、これは明らかに間違っているように思えます。

私が見た別の解決策fetchDataは、Router チェーンの各コンテナ コンポーネントに静的メソッドを追加することです。たとえば、次のようなものです。

FooViewContainer.js

class FooViewContainer extends React.Component {

  static fetchData (query, params, store, history) {
    store.dispatch(hydrateFoo(loadFooByIdSync(params.id)))
  }

  ...

}

サーバー.js

let { query, params } = renderProps
renderProps.components.forEach(comp => 
  if (comp.WrappedComponent && comp.WrappedComponent.fetchData) {
    comp.WrappedComponent.fetchData(query, params, store, history)
  }
})

これよりも良いアプローチがあるに違いないと感じています。それはかなり洗練されていないように見えるだけでなく (.WrappedComponent信頼できるインターフェイスですか?)、高次のコンポーネントでは動作しません。ルーティングされたコンポーネント クラスのいずれかがこれ以外のものによってラップされている場合connect()、動作が停止します。

ここで何が欠けていますか?

4

2 に答える 2

1

最近、この要件に関する記事を書きましたが、redux-saga を使用する必要があります。redux-thunks の観点からピックアップし、この静的 fetchData/need パターンを使用します。

https://medium.com/@navgarcha7891/react-server-side-rendering-with-simple-redux-store-hydration-9f77ab66900a

このサガのアプローチは、はるかにクリーンで理由付けが簡単だと思いますが、それは私の意見かもしれません:)

于 2016-08-01T14:16:50.093 に答える
0

fetchData元の質問に含めたアプローチよりも、これを行う慣用的な方法はないようです。私にはまだ洗練されていないように見えますが、最初に気付いたよりも問題が少なくなっています。

  • .WrappedComponentは安定したインターフェースですが、リファレンスはとにかく必要ありません。Reduxconnect関数は、静的メソッドを元のクラスからそのラッパーに自動的に巻き上げます。
  • Redux にバインドされたコンテナーをラップする他の高次コンポーネント、静的メソッドをホイスト (またはパススルー) する必要があります。

私が見ていない他の考慮事項があるかもしれませんが、server.jsファイルで次のようなヘルパーメソッドに落ち着きました:

function prefetchComponentData (renderProps, store) {
  let { params, components, location } = renderProps
  components.forEach(componentClass => {
    if (componentClass && typeof componentClass.prefetchData === 'function') {
      componentClass.prefetchData({ store, params, location })
    }
  })
}
于 2016-07-22T08:46:01.887 に答える