missing action
See original GitHub issueI hope I’m not duplicating any issues, but from #50 I got the impression that the use-case of synchronous actions was already resolved in v0.6+.
I’m using a custom middleware to handle API calls, more or less like from the real-world example in redux. So I dispatch an object that might look like
function loadUserData(id) {
return {
types: [USERDATA_PENDING, USERDATA_SUCCESS, USERDATA_FAILURE],
[API]: {
endpoint: '/whatever/user'
},
meta: { userId: id }
}
}
within the api middleware authentication, caching etc will be handled (I know the whole thing could be maybe handled with sagas, but for now we just try to migrate bits and pieces and see how it works out). Right the start of the middleware I dispatch the pending action
...
const [ pending, success, failure ] = action.types
dispatch({ type: pending, meta: action.meta })
...
In my saga now I listen to some user intent action (e.g. SHOW_PROFILE) from a component and try to make related calls along with other operations that might need to happen as a result
...
const { id } = yield take(SHOW_PROFILE)
(typeof id !== 'undefined') ? yield put(loadUserData) : yield put(<someOtherAction>)
const { meta: { userId }} = yield take(USERDATA_PENDING)
history.push(`/app/someurl/${userId}`)
...
in this scenario I miss the pending action, and the only way to take it is to wrap the dispatch call in the api middleware with a setTimeout function.
Is this case still a limitation of the library or am I missing something to handle these use-cases correctly?
Issue Analytics
- State:
- Created 8 years ago
- Comments:10 (3 by maintainers)
Top GitHub Comments
@baabgai First let’s understand why you’re missing the action.
First thing to know is that the
put
effect is dispatched asynchronously in a microtask (you’ll see why shortly); i.e. something like thiswhat that means in your code is that
yield put(loadUserData(...))
is not executed right away but shceduled in a microtask. so the current flow will first terminate (e.g. the code dispatching the action to the Saga) then the dispatch will be executed.There are various reasons for this but in this case your example doesn’t work precisely because of synchronous dispatching. Here is why
Suppose that redux-saga executes the put effects syncronously (i.e right away)
What will happen in your example above is the following sequence
SHOW_PROFILE
put(loadUserData(...))
effectloadUserData(...)
action synchronouslydispatch({ type: pending, meta: action.meta })
. and this is the issue: because we’re still in the middle of the first dispatch.In other terms we’ve this call sequence
so the issue here is precisely because the sequence is synchronous, when the Saga is calling put; It’ll have to wait for the dispatch call to return (Just as you wait for a
val = func()
). But since the middleware action is dispatched inside the first dispatch, the Saga will not take the nested action because it’s still waiting for the outer dispatch call to return.and this is exactly what’s happening in your example above: even if the Saga dispatches the action asynchronously, the whole sequence above will happen inside the same microtask (the dispatch and the nested dispatch).
So the solution is to dispatch the middleware action after the the saga action and not inside of it. And to do this you have also to dispatch your action asynchronously
0.6 solved the case for synchronously taking actions: i.e. the take effects will be resolved in the same task as when the action was dispatched (e.g. from a Component event handler). But the case for sync/async dispatches is more complicated (conceptually and not technically) because of the nested dispatch issue above.
I opted for the async dispatch because it provides a more predictable behavior for most of use cases. For example a common use case is for Sagas to dispatch actions at the start of the application
The Saga dispatches an action right at the start. If we dispatch the action synchronously, it won’t work because the code above will execute inside the
applyMiddleware(...)
call, i.e. before even thecreateStore
returns.You can also find more examples in those test cases
TBH I can’t answer this. I agree it has to do with the event model of Sagas (take/put). But I think it’s also due to how Redux dispatching works.
Will this issue be gone if Redux flattened itself the dispatches (for example by ordering them in microtasks) ? I don’t know. But I’d like to hear what @gaearon is thinking of this
Thanks for the suggestions, but this won’t work because I need a key which is included in the meta tag of the action to get the navigation correct. I forgot to include these requirements in the original post (now its updated). I’m sure there might be other ways to work around the problem, but I was also curious to know if this is somehow expected behavior or if I been missing something, especially since I’ve got the impression that after v0.6 this might work.