[英]Server Side React Initial Render causes duplicated async calls
I'm using react-router 2.0 on the server.我在服务器上使用 react-router 2.0。 Some of my top-level route components depend on async data to be fetched and uses Redux to manage state.
我的一些顶级路由组件依赖于要获取的异步数据并使用 Redux 来管理状态。 To deal with this, I'm using a static
fetchData()
method to return a Promise.all
of async actions as suggested in the official Redux docs .为了解决这个问题,我使用了一个静态的
fetchData()
方法来返回一个Promise.all
的异步操作,如官方 Redux 文档中所建议的。 It works pretty well - the initial render from the server comes with the appropriate data in the Redux store.它工作得很好——来自服务器的初始渲染带有 Redux 存储中的适当数据。
My issue comes from how to write these async calls in a way that will work on both the initial render on the server and subsequent renders on the client as different routes get loaded.我的问题来自如何编写这些异步调用,这种方式既适用于服务器上的初始渲染,又适用于客户端上的后续渲染,因为加载了不同的路由。 Currently, I make calls to the static
fetchData()
method in my top-level component's componentDidMount()
, but this would cause the fetchData()
to be called once the component mounts on the client.目前,我在顶级组件的
componentDidMount()
调用静态fetchData()
方法,但是一旦组件安装在客户端上,这将导致fetchData()
。 This is what I want when navigating to the route from another, but not on the initial render as we already made this call on the server.这是我从另一个导航到路由时想要的,但不是在初始渲染时,因为我们已经在服务器上进行了这个调用。
I've been struggling with writing my data fetching logic in a way that:我一直在努力以一种方式编写我的数据获取逻辑:
I was thinking of injecting a prop like { serverRendered: true }
into the initial component but I only have access to the <RouterContext>
when calling renderToString()
.我正在考虑将像
{ serverRendered: true }
这样的道具注入初始组件,但在调用renderToString()
时我只能访问<RouterContext>
。 I took a peek at the source for but it doesn't seem like I can pass the property from that JSX tag either.我查看了源代码,但似乎我也无法从该 JSX 标记传递该属性。
I guess my questions would be:我想我的问题是:
<RouterContext>
so that it passes down to the 'parent' top-level route component?<RouterContext>
注入一个属性,以便它向下传递到“父”顶级路由组件? If not in <RouterContext>
, is there another way to inject the property in?<RouterContext>
,是否有另一种方法可以注入该属性?fetchData()
so the initial render won't cause fetchData()
to be called on both the server and client?fetchData()
以便初始渲染不会导致fetchData()
在服务器和客户端上都被调用? Here's a code sample of my server.jsx, the fetchData() handling middleware and an example top-level compnent.这是我的 server.jsx、fetchData() 处理中间件和示例顶级组件的代码示例。
I faced up with the same issue.我遇到了同样的问题。 Clearing some global value with
setTimeout
or checking Redux store for data existence didn't look good for me.使用
setTimeout
清除某些全局值或检查 Redux 存储中是否存在数据对我来说并不好。
I decided to check on which paths initial data was loaded, put this array as global value and check matches for route on componentDidMount
.我决定检查加载了哪些路径初始数据,将此数组作为全局值并检查
componentDidMount
路由匹配。
My server index.js我的服务器 index.js
const pathsWithLoadedData= []; // here I store paths
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
if (route.loadData) {
// if route loads data and has path then save it
route.path && pathsWithLoadedData.push(route.path);
return route.loadData(store);
} else {
return Promise.resolve(null);
}
})
My render template我的渲染模板
<body>
...
<script>
window.INITIALLY_LOADED_PATHS = ${JSON.stringify(pathsWithLoadedData)};
</script>
...
</body>
Some componentDidMount function一些 componentDidMount 函数
componentDidMount () {
if (!wasFetchedInitially(this.props.route.path)) {
this.props.fetchUsers();
}
}
And helper和帮手
export const wasFetchedInitially = componentPath => {
const pathIndex = window.INITIALLY_LOADED_PATHS.indexOf(componentPath);
if (pathIndex < 0) return false;
window.INITIALLY_LOADED_PATHS.splice(pathIndex, 1);
return true;
};
我注入一个全局变量window.__INITAL_FROM_SERVER__ = true
并从客户端使用setTimeout
清除它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.