我有一个使用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()
以外的任何东西包裹,那么这将停止工作。
我在这里缺少什么?
答案 0 :(得分:1)
我最近写了一篇关于这个要求的文章,但确实需要使用redux-sagas。它从redux-thunks的角度来看并使用这个静态fetchData / need模式。
我认为这种传奇方法更清晰,更容易推理,但这可能仅仅是我的观点:)
答案 1 :(得分:0)
与我原始问题中包含的fetchData
方法相比,似乎没有更惯用的方法。虽然它对我来说似乎仍然不优雅,但问题比我最初意识到的要少:
.WrappedComponent
是一个稳定的界面,但无论如何都不需要参考。 Redux connect
函数自动将原始类中的所有静态方法提升到其包装器中。我可能还有其他一些注意事项,但我已在我的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 })
}
})
}