How to dispatch multiple actions in ngrx/effect (redux-observable)?
An effect transforms a stream of actions, so you have a stream of actions as input and output. In your example, you map an action to an array of actions. A stream of arrays of actions is not a valid output type. You need to flatten that array, meaning that you do not emit the array itself into the output stream but instead each of its elements.
Instead of:
input: --a-------a------>
output: --[b,c]---[b,c]-->
You should do:
input: --a-------a------>
output: --b-c-----b-c-->
For flattening an Observable of array into Observables of each element, you can use one of the operators mergeMap
, switchMap
, exhaustMap
. In most cases, mergeMap
will be the right choice. If you want to learn more about these operators, have a look at this answer.
@Effect()
register$: Observable<Action> = this.actions$.pipe(
ofType(AuthActionTypes.REGISTER_REQUEST),
mergeMap((action: RegisterRequest) => {
// check for register request success
return [
new RegisterSuccess(),
new LoginRequest(action.payload)
]
})
);
I have had same situation (and assuming NgRx 10 or higher), I have a different perspective, more fundamental way how to use effects. Triggering sequentially multiple actions in one place, specially within a single effect, is anti-pattern. In essense, its important to keep a consistent general flow of application state in NgRx of actions and potentials reductions. Just as the NgRx architecture foresees it.
Following the 3 effect rules will help already to avoid difficult situations:
- Name effects to be the name of the effect
- Make your effect only do one thing
- Emit only one action
That way, it helps you to follow the separation of concerns design pattern which of course also help you the NgRx effects to become a lot more unit testable.
Back to your example, you can simple decouple what you wanted to do (2 additional actions) with an in-between-proxy action.
In your case it seems that you may not need even your original effect dispathMultipleActions$, unless special logic within appears. (which perhaps, may belong into a state Reducer, which is even more unit testable).
Assuming that the ActionTypes.UpdateSomething has a array payload object already, you could split your dispathMultipleActions$ into single ones, so you could do something like this:
@Effect()
deleteAction$ = this.actions$.pipe(
ofType(ActionTypes.UpdateSomething),
concatMap(from(new Promise((array) => {
array.forEach(item => {
if (item > 3) {
//do something
}
});
}))),
{dispatch: false}
);
@Effect()
changeAction$ = this.actions$.pipe(
ofType(ActionTypes.UpdateSomething),
concatMap(from(new Promise((array) => {
array.forEach(item => {
if (item <= 3) {
//do something
}
});
}))),
{dispatch: false}
);