我正在使用 redux-saga 实现服务器端渲染.
我正在关注 "real world" example在 redux-saga 存储库中提供。
- > node.js入口点使用 react.js
renderToString
以呈现应用程序。 - 呈现应用程序会触发
componentWillMount
,它会调度操作GET_GEOLOCATION
和GET_DATE
。这些异步操作将通过SET_GEOLOCATION
和SET_DATE
解决。 renderToString
完成呈现应用程序;END
操作终止 saga 监听器
问题是 SET_GEOLOCATION
和 SET_DATE
本身用于put
新操作 GET_MOVIES
。然而,当 SET_GEOLOCATION
和 SET_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 thantake
?
为了回答我自己的问题,我需要观察所有已解决的问题 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/