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

标签 javascript react-redux redux-saga

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

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

  1. > 入口点使用 renderToString 以呈现应用程序。
  2. 呈现应用程序会触发 componentWillMount,它会调度操作 GET_GEOLOCATIONGET_DATE。这些异步操作将通过 SET_GEOLOCATIONSET_DATE 解决。
  3. renderToString 完成呈现应用程序; END 操作终止 saga 监听器

问题是 SET_GEOLOCATIONSET_DATE 本身用于put 新操作 GET_MOVIES。然而,当 SET_GEOLOCATIONSET_DATE 被调用时,saga 监听器不再活跃(我们在 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 状态的 sagas 之后?

最佳答案

How to delay store.close until after there are no sagas that are in the state other than take?

为了回答我自己的问题,我需要观察所有已解决的问题 put .我可以使用 Saga Monitor 来做到这一点.

Saga Monitor 可以在创建 redux-saga 中间件时配置。对于我们的用例,它需要在操作被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;

然后而不是同步停止 saga...

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 只有在所有异步效果都被解析时才会被调用。

关于javascript - 渲染服务器端时如何处理多个相互依赖的传奇?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43834756/

相关文章:

javascript - 使用 javascript Jquery 更改 URL

javascript - 了解异步 javascript

reactjs - saga中api调用成功后如何更改url

javascript - 使用转换过滤器根据搜索输入过滤 ng-repeat

c# - 将二维数组发送到 Web API 服务

javascript - mxGraph:如何将边缘标签定位在底部

javascript - Redux以前的状态已经有了新的状态值无法理解为什么

reactjs - 警告 : Unexpected key "A" found in previous state received by the reducer

javascript - React/redux 具有相同容器的多个组件

javascript - 试图访问 facebook graph api 回调中的生成器函数功能