javascript - ReactJS服务端渲染、setTimeout问题

标签 javascript reactjs redux settimeout server-side-rendering

在我的 ReactJS 应用程序上,我使用 setTimeout 来推迟一些 Redux 操作:

export const doLockSide = (lockObject) => (dispatch) => {
  const timerId = setTimeout(() => {
    dispatch({
      type: CONSTANTS.TOPICS_SET_CURRENT_TOPIC_LOCKED_SIDE,
      payload: { id: lockObject.topicId, side: lockObject.side, locked: false }
    });
  }, lockObject.unlockTimeout);

  dispatch({
    type: CONSTANTS.TOPICS_SET_CURRENT_TOPIC_LOCKED_SIDE,
    payload: { id: lockObject.topicId, side: lockObject.side, timerId, locked: true }
  });
};

lockObject 来自服务器,因此此代码是异步 Redux 操作链的一部分。它工作得很好,但是当我尝试使此功能成为服务器端渲染过程的一部分时,它破坏了应用程序。我了解浏览器和 NodeJS 运行时环境之间的差异以及其 setTimeout 实现之间的差异。具体来说,我的 timerId 无法被 Node 处理,因为它是一个对象,而我的 Redux reducer 将它视为整数。但主要问题是在服务器端渲染期间 Node 会在服务器端触发 setTimeout 回调...

问题。我有一些基于 redux 的进程,在某些情况下(包括应用程序启动)应该推迟。如何才能满足服务端渲染的要求?

最佳答案

经过一些研究,我能够应用以下方法。

1) 如果是服务器端渲染,则将延迟操作数据推送到某个特殊存储中,如果是浏览器,则“按原样”运行:

import { _postRender } from '../utils/misc';

const doLockSideUI = (dispatch, lockObject) => {
  // the body of previous version of doLockSide inner function
  const timerId = setTimeout(() => {/*...*/}, lockObject.unlockTimeout);
  dispatch(/*...*/);
};

export const doLockSide = (lockObject) => (dispatch) => {
  if(typeof window === 'undefined') { // server-side rendering case
    _postRender.actions.push({
      name: 'doLockSide',
      params: lockObject
    });
  }
  else { // Browser case
    doLockSideUI(dispatch, lockObject);
  }
};

其中 utils/misc.js 具有以下实体:

// to run actions on the Client after server-side rendering
export const _postRender = { actions: [] }; 

2) 在服务器上,我已从 utils/misc.js 形式导入了 _postRender 对象,并将其推送到 render 当所有 redux-store 数据依赖关系都已解决时的参数:

const markup = renderToString(/*...*/);
const finalState = store.getState();
const params = { markup, finalState, postRender: { ..._postRender } };
_postRender.actions = []; // need to reset post-render actions
return res.status(status).render('index', params);

_postRender.actions 必须被清理,否则 p.1 中的 _postRender.actions.push 会在每次客户端重新加载时一次又一次地填充它。

3) 然后,我以与预加载状态相同的方式提供了渲染后操作。就我而言,它是 index.ejs 模板:

<div id="main"><%- markup %></div>
<script>
  var __PRELOADED_STATE__ = <%- JSON.stringify(finalState) %>;
  var __POST_RENDER__ = <%- JSON.stringify(postRender) %>;
</script>

4) 现在我需要使用给定的参数调用我的 __POST_RENDER__ 操作。为此,我更新了根组件的 did-mount Hook ,并分派(dispatch)了一个处理渲染后操作列表的附加操作:

componentDidMount() {
  console.log('The App has been run successfully');
  if(window.__POST_RENDER__ && window.__POST_RENDER__.actions.length) {
    this.props.dispatch(runAfterRender(window.__POST_RENDER__.actions));
  }
}

其中 runAfterRender 是从 ../actions/render 导入的新操作:

import { doLockSide } from './topic'

export const runAfterRender = (list) => (dispatch) => {
  list.forEach(action => {
    if(action.name === 'doLockSide') {
      dispatch(doLockSide(action.params));
    }
    // other actions?
  });
};

如您所见,这只是一个草稿,我被迫从 p.1 导入 doLockSide 操作并显式调用它。我猜想在服务器端渲染之后可能会有一个可以在客户端上调用的可能操作列表,但这种方法已经有效。请问有没有更好的办法...

关于javascript - ReactJS服务端渲染、setTimeout问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47855673/

相关文章:

javascript - Google map - 多个标记 - 1 个 InfoWindow 问题

javascript - 如何在映射并将 onClick 函数作为 Prop 传递后定位特定元素

javascript - 是否有标准方法来路由 setState redux 操作?

react-router - 获取异步组件时 React 路由器抛出错误

php - 将数据从输入字段传输到新页面

javascript - 在 jquery Ajax 中使用多个引导按钮

javascript - 如何创建一个新的 react 组件实例

javascript - 无法读取未定义的属性 'rejected' | ReactJS

javascript - HTML 版本的 AnyChart 可以与 React 一起使用吗?

javascript - Angular 2 JS(RC4 及更高版本)中的嵌套表单