如何在渲染服务器端时处理多个相互依赖的传奇?

时间:2017-05-07 17:52:28

标签: javascript react-redux redux-saga

我正在使用实现服务器端呈现。

我正在关注redux-saga存储库中提供的"real world" example

  1. 入口点使用 renderToString来呈现应用。
  2. 呈现应用程序触发器componentWillMount,它会调度操作GET_GEOLOCATIONGET_DATE。这些异步操作将通过SET_GEOLOCATIONSET_DATE解决。
  3. renderToString完成呈现应用程序; END操作会终止传奇听众
  4. 问题在于SET_GEOLOCATIONSET_DATE本身用于put新动作GET_MOVIES。但是,在调用SET_GEOLOCATIONSET_DATE时,传奇侦听器不再处于活动状态(我们在renderToString之后终止它)。因此,在调度GET_MOVIES时,不会选择GET_MOVIES操作,SET_MOVIE将永远不会发生。

    服务器代码:

    app.get('*', (req, res) => {
      const history = createMemoryHistory({
        initialEntries: [
          req.url
        ]
      });
      const store = configureStore(undefined, history);
      const context = {};
    
      const rootComponent = <Provider store={store}>
        <StaticRouter context={context} location={req.url}>
          <Route component={RootRoute} />
        </StaticRouter>
      </Provider>;
    
      store
        .runSaga(rootSaga).done
        .then(() => {
          const body = renderToString(rootComponent);
          const response = renderHtml(body, store);
    
          res
            .send(response);
        })
        .catch((error) => {
          res
            .status(500)
            .send(error.message);
        });
    
      // Force componentWillMount to issue saga effects.
      renderToString(rootComponent);
    
      store.close();
    });
    

    萨加斯:

    const watchNewSearchCriteria = function *(): Generator<*, *, *> {
      yield takeLatest([
        SET_GEOLOCATION,
        SET_DATE
      ], function *() {
        const {
          coordinates,
          date
        } = yield select((state) => {
          return {
            coordinates: state.movieEventsView.location ? state.movieEventsView.location.coordinates : null,
            date: state.movieEventsView.date
          };
        });
    
        if (!coordinates || !date) {
          return;
        }
    
        yield put(getMovies({
          coordinates,
          date
        }));
      });
    };
    
    const watchGetMovies = function *() {
      yield takeLatest(GET_MOVIES, function *(action) {
        const result = yield call(getMovies, action.payload);
    
        yield put(setMovies(result));
      });
    };
    

    如果没有store.close以外的州内没有传奇,那么如何推迟take

1 个答案:

答案 0 :(得分:4)

  

如果没有store.close以外的州内没有传奇,那么如何推迟take

要回答我自己的问题,我需要观察任何put的解决方案。我可以使用Saga Monitor

来完成此操作

可以在创建redux-saga中间件时配置Saga Monitor。对于我们的用例,它需要跟踪某个操作put,并在解析/拒绝/取消时将其从索引中删除。

const activeEffectIds = [];

const watchEffectEnd = (effectId) => {
  const effectIndex = activeEffectIds.indexOf(effectId);

  if (effectIndex !== -1) {
    activeEffectIds.splice(effectIndex, 1);
  }
};

const sagaMiddleware = createSagaMiddleware({
  sagaMonitor: {
    effectCancelled: watchEffectEnd,
    effectRejected: watchEffectEnd,
    effectResolved: watchEffectEnd,
    effectTriggered: (event) => {
      if (event.effect.CALL) {
        activeEffectIds.push(event.effectId);
      }
    }
  }
});

我们需要从商店的消费者那里访问它,因此我将activeEffectIds分配给商店实例:

store.runSaga = sagaMiddleware.run;

store.close = () => {
  store.dispatch(END);
};

store.activeEffectIds = activeEffectIds;

然后不是同步停止传奇...

renderToString(rootComponent);
store.close();

我们需要延迟store.close,直到store.activeEffectIds.length为0。

const realDone = () => {
  setImmediate(() => {
    if (store.activeEffectIds.length) {
      realDone();
    } else {
      store.close();
    }
  });
};

// Force componentWillMount to issue saga effects.
renderToString(rootComponent);

realDone();

现在只有在解决了所有异步效果后才会调用store.close