渲染服务器端时如何处理多个相互依赖的传奇? [英] How to handle multiple interdependent sagas when rendering server-side?

查看:52
本文介绍了渲染服务器端时如何处理多个相互依赖的传奇?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 .

我正在关注 "真实世界"示例在 redux-saga 存储库中提供.

I am following the "real world" example provided in the redux-saga repository.

  1. 条目-point 使用 renderToString 来呈现应用程序.
  2. 渲染应用程序会触发 componentWillMount,它会分派操作 GET_GEOLOCATIONGET_DATE.这些异步操作将通过 SET_GEOLOCATIONSET_DATE 解决.
  3. renderToString 完成渲染应用程序;END 动作终止 saga 监听器
  1. node.js entry-point uses react.js renderToString to render the application.
  2. rendering the application triggers componentWillMount, which dispatches actions GET_GEOLOCATION and GET_DATE. These async actions will resolve with SET_GEOLOCATION and SET_DATE.
  3. renderToString finishes rendering the application; END action terminates the saga listeners

问题在于SET_GEOLOCATIONSET_DATE 本身用于put 一个新动作GET_MOVIES.然而,当 SET_GEOLOCATIONSET_DATE 被调用时,saga 监听器不再处于活动状态(我们在 renderToString 之后终止了它).因此,虽然 GET_MOVIES 将被分派,GET_MOVIES 动作将不会被选择,SET_MOVIE 将永远不会发生.

The problem is that SET_GEOLOCATION and SET_DATE themselves are used to put a new action GET_MOVIES. However, by the time the SET_GEOLOCATION and SET_DATE are called, the saga listeners are no longer active (we terminated it after renderToString). Therefore, while GET_MOVIES will be dispatched, the GET_MOVIES action will not be picked and SET_MOVIE will never happen.

服务器代码:

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 之外的其他状态的 saga 之后?

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

推荐答案

如何延迟 store.close 直到没有除了 take 之外的其他状态的 saga 之后?

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

要回答我自己的问题,我需要观察任何已经放置.我可以使用 Saga Monitor 做到这一点.

To answer my own question, I need to observe resolution of anything thats been put. I can do this using the Saga Monitor.

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

Saga Monitor can be configured at the time of creating the redux-saga middleware. For our use case, it needs to track whenever an action has been put and remove it from the index when it has been resolved/ rejected/ cancelled.

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 分配给商店实例:

We need to access this from the consumer of the store, therefore I assign activeEffectIds to the store instance:

store.runSaga = sagaMiddleware.run;

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

store.activeEffectIds = activeEffectIds;

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

Then instead of synchronously stopping the saga...

renderToString(rootComponent);
store.close();

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

we need to delay store.close until store.activeEffectIds.length is 0.

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

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

realDone();

现在 store.close 只有在所有异步影响都解决后才会被调用.

Now store.close is called only when all the asynchronous effects are resolved.

这篇关于渲染服务器端时如何处理多个相互依赖的传奇?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆