Possible to await all side-effects? #3922
-
When using a listener (https://redux-toolkit.js.org/api/createListenerMiddleware) to add side-effects to an action, is there a way for the caller to await for both the action and the side effect? Or perhaps this doesn't make sense conceptually, and it's better to move the side effect as a regular thunk, then create a wrapper thunk where it will await both the action and supposed side effect? Specifically, I'm trying to do an "auto-fetch" where when --- in some thunk ---
dispatch(someSlice.actions.setStateA('blah'));
// effect runs the fetch ...
await dispatch(doSomeActionThatDependsOnTheFetchedValue());
// effect runs the fetch ... done |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
There's no direct way to coordinate. Dispatching a thunk can return a promise, but here you're dispatching a plain action, and then the listener kicks off the effect as a basically independent piece of logic. There's nothing that gets returned from The closest option, actually, would be to start another listener that watches for the expected state change, which we've also sort of got encapsulated in a /**
* Attach a one-shot listener to the store, and resolves to `true` when `predicate` matches (or `false` if given `timeout` expires), then unsubscribes itself
* @param predicate Condition to match - receives action, state, and originalState (before action was dispatched)
* @param timeout Time in ms to wait before resolving promise to `false`.
* @returns Promise to wait on.
*/
export const listenForCondition = <State>(
predicate: AnyListenerPredicate<State>,
timeout?: number
): ThunkAction<Promise<boolean>, State, unknown, AnyAction> => (dispatch) =>
new Promise<boolean>((resolve) => {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
const unsub = dispatch(
(addListener as TypedAddListener<State>)({
predicate,
effect: (action, { unsubscribe }) => {
unsubscribe();
resolve(true);
if (typeof timeoutId !== undefined) {
clearTimeout(timeoutId);
}
},
})
);
if (timeout) {
timeoutId = setTimeout(() => {
resolve(false);
unsub();
}, timeout);
}
}); which you would use as: await dispatch(listenForCondition(
(action, currState, prevState) => {
// return true when the appropriate state condition happens
}
)) |
Beta Was this translation helpful? Give feedback.
There's no direct way to coordinate. Dispatching a thunk can return a promise, but here you're dispatching a plain action, and then the listener kicks off the effect as a basically independent piece of logic. There's nothing that gets returned from
dispatch
here.The closest option, actually, would be to start another listener that watches for the expected state change, which we've also sort of got encapsulated in a
listenForCondition
snippet that was listed over here: