【问题标题】:How to hydrate server-side parameters with React + Redux如何使用 React + Redux 水合服务器端参数
【发布时间】:2016-11-21 04:20:23
【问题描述】:

我有一个使用 Redux 和 React Router 的通用 React 应用程序。我的一些路由包含在客户端上将触发 AJAX 请求以混合数据以供显示的参数。在服务器上,这些请求可以同步完成,并在第一个请求时呈现。

我遇到的问题是:当在路由组件上调用任何生命周期方法(例如componentWillMount)时,调度将在第一次渲染中反映的 Redux 操作为时已晚。

这是我的服务器端渲染代码的简化视图:

routes.js

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

server.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() 的第一次调用中,这意味着它们不会反映在页面请求中传递的内容中。

React Router 传递的 id 参数本身对第一次渲染没有用处。我需要同步将该值水合为适当的对象。我应该把这种补水放在哪里?

一种解决方案是将其内嵌在render() 方法中,例如在服务器上调用它。这对我来说显然是不正确的,因为 1)它在语义上没有意义,并且 2)它收集的任何数据都不会正确地发送到商店。

我看到的另一个解决方案是在路由器链中的每个容器组件中添加一个静态fetchData 方法。例如像这样:

FooViewContainer.js

class FooViewContainer extends React.Component {

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

  ...

}

server.js

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

我觉得肯定有比这更好的方法。它不仅看起来相当不优雅(.WrappedComponent 是一个可靠的接口吗?),而且它也不适用于高阶组件。如果任何路由组件类被 connect() 以外的任何东西包裹,这将停止工作。

我在这里错过了什么?

【问题讨论】:

    标签: reactjs react-router react-redux react-router-redux


    【解决方案1】:

    似乎没有比我在原始问题中包含的fetchData 方法更惯用的方法了。虽然它在我看来仍然不优雅,但它的问题比我最初意识到的要少:

    • .WrappedComponent 是一个稳定的接口,但无论如何都不需要引用。 Redux connect 函数自动将原始类中的所有静态方法提升到其包装器中。
    • 包装 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 })
        }
      })
    }
    

    【讨论】:

      【解决方案2】:

      我最近写了一篇关于这个需求的文章,但是它确实需要使用 redux-sagas。从 redux-thunk 的角度来看,它确实采用了这种静态 fetchData/need 模式。

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

      我认为这种传奇方法更清晰,更易于推理,但这可能只是我的看法 :)

      【讨论】:

        猜你喜欢
        • 2016-03-26
        • 1970-01-01
        • 2023-03-11
        • 2017-08-13
        • 2016-05-22
        • 2020-12-18
        • 2020-06-07
        • 2018-03-29
        • 2021-05-09
        相关资源
        最近更新 更多