渲染服务器端时如何处理多个相互依赖的传奇? [英] How to handle multiple interdependent sagas when rendering server-side?
问题描述
我正在使用 实现服务器端渲染redux-saga.
我正在关注 "真实世界"示例在 redux-saga 存储库中提供.
I am following the "real world" example provided in the redux-saga repository.
- node.js 条目-point 使用 react.js
renderToString
来呈现应用程序. - 渲染应用程序会触发
componentWillMount
,它会分派操作GET_GEOLOCATION
和GET_DATE
.这些异步操作将通过SET_GEOLOCATION
和SET_DATE
解决. renderToString
完成渲染应用程序;END
动作终止 saga 监听器
- node.js entry-point uses react.js
renderToString
to render the application. - rendering the application triggers
componentWillMount
, which dispatches actionsGET_GEOLOCATION
andGET_DATE
. These async actions will resolve withSET_GEOLOCATION
andSET_DATE
. renderToString
finishes rendering the application;END
action terminates the saga listeners
问题在于SET_GEOLOCATION
和SET_DATE
本身用于put
一个新动作GET_MOVIES
.然而,当 SET_GEOLOCATION
和 SET_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 thantake
?
要回答我自己的问题,我需要观察任何已经放置代码>
.我可以使用 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屋!