【发布时间】: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