在我的 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/