Server Side React Initial Render导致重复的异步调用

时间:2016-02-11 14:03:52

标签: javascript reactjs react-router serverside-javascript isomorphic-javascript

我在服务器上使用react-router 2.0。我的一些顶级路由组件依赖于要获取的异步数据,并使用Redux来管理状态。为了解决这个问题,我使用静态fetchData()方法返回the official Redux docs中建议的Promise.all异步操作。它工作得很好 - 服务器的初始渲染带有Redux存储中的相应数据。

我的问题来自如何以一种方式编写这些异步调用,这种方式既可以在服务器上进行初始渲染,也可以在不同路由加载时在客户端上进行后续渲染。目前,我在顶级组件的fetchData()中调用静态componentDidMount()方法,但这会导致在组件安装到客户端后调用fetchData()。这是我在从另一个路由导航到路由时所需要的,但不是在初始渲染上,因为我们已经在服务器上进行了此调用。

我一直在努力以下列方式编写数据获取逻辑:

  1. 仅在服务器上的初始渲染时调用一次,而不是在初始客户端渲染上调用
  2. 当用户从另一条路线导航时将进行渲染。
  3. 我在考虑将{ serverRendered: true }这样的道具注入初始组件,但在调用<RouterContext>时我只能访问renderToString()。我看了一眼源代码,但似乎我不能从JSX标签传递属性。

    我想我的问题是:

    1. 有没有办法在<RouterContext>中注入一个属性,以便传递给'父'顶层路由组件?如果不在<RouterContext>,是否有其他方式注入该属性?
    2. 如果无法#1,是否有更好的方法来处理fetchData(),因此初始渲染不会导致在服务器和客户端上调用fetchData()
    3. Here’s a code sample of my server.jsx, the fetchData() handling middleware and an example top-level compnent.

2 个答案:

答案 0 :(得分:1)

我面临着同样的问题。用setTimeout清除一些全局值或检查Redux存储中是否存在数据对我来说并不好。

我决定检查在初始数据加载的路径上,将此数组作为全局值,并检查componentDidMount上的路由是否匹配。

我的服务器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);
        }
    })

我的渲染模板

<body>
   ...
   <script> 
      window.INITIALLY_LOADED_PATHS = ${JSON.stringify(pathsWithLoadedData)};
   </script>
   ...   
</body>

某些componentDidMount函数

componentDidMount () {
    if (!wasFetchedInitially(this.props.route.path)) {
        this.props.fetchUsers();
    }
}

和助手

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;
};

答案 1 :(得分:0)

我注入了一个全局变量window.__INITAL_FROM_SERVER__ = true,并从客户端用setTimeout清除了该变量。