使用 redux 和 react 进行异步服务器端渲染

Posted

技术标签:

【中文标题】使用 redux 和 react 进行异步服务器端渲染【英文标题】:Async server-side rendering using redux & react 【发布时间】:2018-03-29 08:55:52 【问题描述】:

我很难使用ReactReduxRedux-ThunkReact-Router 获得一个有效的服务器端渲染解决方案。

目前,我的解决方案遵循基本准则和这篇文章在客户端运行:https://codepen.io/stowball/post/a-dummy-s-guide-to-redux-and-thunk-in-react。为简单起见,我将使用该博客中的代码作为示例。唯一的改变是我在reducer 函数items 中添加了一个console.log("X");,这样我就知道它什么时候被调用了。结果函数为:

export function items(state = [], action) 
    switch (action.type) 
        case 'ITEMS_FETCH_DATA_SUCCESS':
            console.log('X');
            return action.items;

        default:
            return state;
    

我还设置了itemsFetchData 函数来返回承诺,变成:

export function itemsFetchData(url) 
    return (dispatch) => 
        dispatch(itemsIsLoading(true));

        return fetch(url)
            .then((response) => 
                if (!response.ok) 
                    throw Error(response.statusText);
                

                dispatch(itemsIsLoading(false));

                return response;
            )
            .then((response) => response.json())
            .then((items) => dispatch(itemsFetchDataSuccess(items)))
            .catch(() => dispatch(itemsHasErrored(true)));
    ;

因为我需要服务器端渲染。我设置 Express 来使用我的中间件 handleRender,而后者又调用返回 html 字符串的 renderFullPage。可以假定 Express 实现是正确的。我的handleRender 如下所示

export function handleRender(req, res) 
  // Create a new Redux store instance
  const store = configureStore();

  const  dispatch  = store;

  dispatch(itemsFetchData(''http://5826ed963900d612000138bd.mockapi.io/items'')).then(() => 
    console.log("Y");
    // Render the component to a string
    const html = renderToString(
      <Provider store=store>
        <div id="app">
          <StaticRouter context= location=req.url>
            <Main />
          </StaticRouter>
        </div>
      </Provider>
    );

    // Grab the initial state from our Redux store
    const preloadedState = store.getState();

    // Send the rendered page back to the client
    res.send(renderFullPage(html, preloadedState));
  );

使用上面的代码,Y 被打印到控制台,但X 从未被打印,这意味着减速器函数没有被调用。如果我从我的handleRender 中的promise 中删除then,从而变成:

dispatch(itemsFetchData(''http://5826ed963900d612000138bd.mockapi.io/items''));
console.log("Y");
// ... remaining code remains unaltered

reducer 函数被正确调用并且 Redux 存储正确更新,但是由于这是异步的,handleRender 已经返回了 HTML。

任何帮助将不胜感激。这是漫长的一天。

【问题讨论】:

我建议使用next.js。repo 提供了许多示例(包括 redux 的使用)并且服务器也是可定制的。 挑战服务器端渲染的要求是否为时已晚?我在一个带有 next.js 和 redux 的项目中工作,在返回服务器渲染页面之前执行诸如获取登录用户帐户数据之类的事情是一件很痛苦的事情。我们最终构建了一个普通的 SPA。服务器端渲染非常适合与 SEO 相关的页面和移动网站,以加快初始页面加载速度。对于其他所有内容,尤其是具有用户身份验证的页面,它并不是那么有益。 服务器端渲染是一项要求。我不会深入探讨 s-s-r 的优点,因为它是题外话,但我同意,因为使用 react/redux 感觉它比它应该/过去要复杂得多。 【参考方案1】:

造成混淆的原因是在调用itemsFetchData 之后注入了reducer。仅在使用then 时才注意到该问题,因为没有它,仍然包含注入reducer 的组件Main,并且一切看起来都很正常。当它依赖then 时,我最终发现then 无法调度,因为没有reducer,并且Main 组件不包括在内,因为它正在等待调度。

为了解决这个问题,我在combineReducers 中加入了reducer。

就像我说的,这是漫长的一天。

【讨论】:

以上是关于使用 redux 和 react 进行异步服务器端渲染的主要内容,如果未能解决你的问题,请参考以下文章

使用 React 和 Redux 进行异步图像加载

如何在 React/redux 中进行服务器端渲染?

react-redux s-s-r 异步 api 调用未按预期工作

如何在逻辑上结合 react-router 和 redux 进行客户端和服务器端渲染

[Redux/Mobx] 在React中你是怎么对异步方案进行选型的?

使用 Redux + React Router 的 React App 中的异步数据流?