[英]ReactJS server-side rendering, setTimeout issue
On my ReactJS App I used setTimeout
to defer some Redux action: 在我的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 }
});
};
The lockObject
comes from the server, so this code is a part of async Redux actions chain. lockObject
来自服务器,因此此代码是异步Redux操作链的一部分。 It worked fine, but when I tried to make this functionality to be a part of server side rendering process, it broke the App. 它工作正常,但是当我尝试使此功能成为服务器端渲染过程的一部分时,它破坏了该应用程序。 I understand the difference between Browser and NodeJS runtime environments and the difference between its implementations of
setTimeout
. 我了解Browser和NodeJS运行时环境之间的差异以及
setTimeout
实现之间的差异。 Specifically my timerId
could not be processed by Node due to it's an object, while my Redux reducer treats it as an integer. 具体来说,由于Node是我的对象,因此我的
timerId
无法被Node处理,而我的Redux reducer将其视为整数。 But the main problem is that during server side rendering Node fires setTimeout
callback on the server side... 但是主要问题是在服务器端渲染期间,Node在服务器端触发了
setTimeout
回调...
The question . 问题 。 I have some redux-based proccess that should be deferred in some cases including the App start.
我有一些基于Redux的过程,在某些情况下,包括App启动,应该推迟。 How can I do it satisfying the requirement of server-side rendering?
如何满足服务器端渲染的要求?
After some research I was able to apply the following approach. 经过研究,我能够采用以下方法。
1) Push the deferred action data into some special storage in case of server-side rendering, and run it "as is" in case of Browser: 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);
}
};
Where utils/misc.js
has the following entity: 其中
utils/misc.js
具有以下实体:
// to run actions on the Client after server-side rendering
export const _postRender = { actions: [] };
2) On the server I've imported that _postRender
object form utils/misc.js
and pushed it to render
parameters when all redux-store data dependencies had been resolved: 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
has to be cleaned up, otherwise _postRender.actions.push
from p.1 will populate it again and again each time the Client had been reloaded. _postRender.actions
已被清理,否则_postRender.actions.push
从第1页将每个客户端已经重新加载时间一次又一次地填充它。
3) Then I provided my post-render actions the same way as it is done for preloaded state. 3)然后,我提供了与预加载状态相同的渲染后操作。 In my case it is
index.ejs
template: 就我而言,它是
index.ejs
模板:
<div id="main"><%- markup %></div>
<script>
var __PRELOADED_STATE__ = <%- JSON.stringify(finalState) %>;
var __POST_RENDER__ = <%- JSON.stringify(postRender) %>;
</script>
4) Now I need to call my __POST_RENDER__
actions with given params. 4)现在,我需要使用给定的参数调用
__POST_RENDER__
动作。 For this purpose I updated my root component's did-mount hook and dispatch an additional action which handles the post-render action list: 为此,我更新了我的根组件的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));
}
}
Where runAfterRender
is a new action that is being imported from ../actions/render
: 其中
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?
});
};
As you can see, it's just a draft and I was forced to import doLockSide
action from p.1 and call it explicitly. 如您所见,这只是草稿,我被迫从p.1导入
doLockSide
操作并显式调用它。 I guess there may be a list of possible actions that could be called on the Client after server-side rendering, but this approach already works. 我猜可能在服务器端渲染后可能会在客户端上调用的可能动作列表,但是这种方法已经有效。 I wonder if there is a better way...
我想知道是否有更好的方法...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.