[英]ReactJS server-side rendering, setTimeout issue
在我的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操作链的一部分。 它工作正常,但是当我尝试使此功能成为服务器端渲染过程的一部分时,它破坏了该应用程序。 我了解Browser和NodeJS运行时环境之间的差异以及setTimeout
实现之间的差异。 具体来说,由于Node是我的对象,因此我的timerId
无法被Node处理,而我的Redux reducer将其视为整数。 但是主要问题是在服务器端渲染期间,Node在服务器端触发了setTimeout
回调...
问题 。 我有一些基于Redux的过程,在某些情况下,包括App启动,应该推迟。 如何满足服务器端渲染的要求?
经过研究,我能够采用以下方法。
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)在服务器上,我已将_postRender
对象从utils/misc.js
导入,并在解决了所有redux-store数据依赖关系后,将其推送到render
参数:
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
已被清理,否则_postRender.actions.push
从第1页将每个客户端已经重新加载时间一次又一次地填充它。
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钩子,并调度了一个额外的操作来处理渲染后操作列表:
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
操作并显式调用它。 我猜可能在服务器端渲染后可能会在客户端上调用的可能动作列表,但是这种方法已经有效。 我想知道是否有更好的方法...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.