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

Gaj*_*jus 1 javascript redux-saga react-redux

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

我正在遵循redux-saga存储库中提供的“真实世界”示例

  1. 入口点使用 renderToString呈现应用程序。
  2. 呈现应用程序触发器componentWillMount,从而调度动作GET_GEOLOCATIONGET_DATE。这些异步操作将使用SET_GEOLOCATION和解决SET_DATE
  3. renderToString完成渲染应用程序;END动作终止了传奇听众

问题是,SET_GEOLOCATIONSET_DATE自己来put一个新的动作GET_MOVIES。但是,在调用SET_GEOLOCATION和时SET_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();
});
Run Code Online (Sandbox Code Playgroud)

萨加斯:

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));
  });
};
Run Code Online (Sandbox Code Playgroud)

如何延迟store.close到没有除状态以外的sagas之后take

Gaj*_*jus 5

如何延迟store.close到没有除状态以外的sagas之后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);
      }
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

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

store.runSaga = sagaMiddleware.run;

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

store.activeEffectIds = activeEffectIds;
Run Code Online (Sandbox Code Playgroud)

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

renderToString(rootComponent);
store.close();
Run Code Online (Sandbox Code Playgroud)

我们需要延迟store.closestore.activeEffectIds.length0。

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

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

realDone();
Run Code Online (Sandbox Code Playgroud)

store.close仅在解决所有异步影响时才调用Now 。