问题
So I'm injecting Sagas dynamically when the route loads
path: '/home',
name: 'home',
getComponent(nextState, cb) {
require.ensure([],
require => {
let HomePageReducer = require('./containers/HomePage/reducer').default;
let HomePageSagas = require('./containers/HomePage/sagas').default;
let HomePage = require('./containers/HomePage').default;
injectReducer('home', HomePageReducer);
injectSagas(HomePageSagas);
cb(null, HomePage);
})
},
injectAsyncSagas
goes like this:
export function injectAsyncSagas(store) {
return (sagas) => sagas.map(store.runSaga);
}
where store.runSaga
is sagaMiddleware.run
, created during store creation.
When the route loads the saga is successfully started. It's listening for actions.
export function* mySagaFunction() {
console.log("Start mySagaFunction");
while (true) {
const watcher = yield race({
loadRepos: take(SOME_HOME_PAGE_ACTION),
stop: take(LOCATION_CHANGE), // stop watching if user leaves page
});
if (watcher.stop) {
console.log("Stop mySagaFunction", watcher.stop);
break;
}
//other statements here to handle SOME_HOME_PAGE_ACTION
}
When I load the route, "Start mySagaFunction" is consoled. If I dispatch SOME_HOME_PAGE_ACTION, it successfully does whatever it has to do. When I leave the page, react router calls LOCATION_CHANGE, and Saga consoles "Stop mySagaFunction" implying it successfully quit the while loop.
Then I go to another route and come back to this route. This time LOCATION_CHANGE is dispatched by router, as expected. But Saga is starting and then stopping immediately. My console goes like this:
Start mySagaFunction
Stop mySagaFunction
In Redux dev tools I see only one LOCATION_CHANGE. How do I figure out why the Saga is starting and immediately stopping? It happens only if I come back to the route.
I thought maybe LOCATION_CHANGE was being dispatched late. But no. In my reducers I consoled the action type. Reducer is called first with LOCATION_CHANGE and saga starts and then saga stops.
I'm using Link
to navigate between pages. I tried browserHistory.push(url)
and dispatch(push(url))
as well. Same result.
Note: I got the code for injecting the sagas from react-boilerplate. I have only taken this code and I'm not using the boilerplate itself. I ran the boilerplate and this bug is not happening there. Differences are:
- It's using
selectLocationState
in syncHistoryWithStore. My whole store is not Immutable so I don't need that. - It's using it's own
routeReducer
instead ofrouterReducer
fromreact-router-redux
. I don't know why, maybe because of full immutable store.
Anyways in the boiler plate code I removed the above two points and still the scenario is not occurring.
回答1:
The route had an onEnter redirect and hence LOCATION_CHANGE would be dispatched twice thereby cancelling the saga.
{
path: 'home',
getComponent(nextState, cb) {
require.ensure([], require => {...})
},
indexRoute: {
onEnter: (nextState, replace) => replace('/home/A')
}
}
Workaround for this:
I'm dispatching a LEAVE_PAGE action in onLeave hook for every page that injects sagas
onLeave: ()=> {
store.dispatch({type: LEAVE_HOME_PAGE});
}
and in the saga watch for that particular action:
const watcher = yield race({
loadRepos: take(SOME_HOME_PAGE_ACTION),
stop: take(LEAVE_HOME_PAGE), // stop watching if user leaves page
});
来源:https://stackoverflow.com/questions/37638549/redux-saga-stopped-by-location-change-too-early