簡體   English   中英

ReactJS服務器端渲染,setTimeout問題

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM