Don't let your Effects die (catchSwitchMapError operator)
This post assumes you have a good understanding of angular, ngrx, Effects and rxjs already. If thats not the case, it might not be very interesting for you :)
A common use for ngrx Effects is integrating calls to a REST API into the redux pattern. Typically the workflow would look something like this:
1 | requestBananaEffect = createEffect(() => |
- Dispatch an Action
BananaActions.RequestBanana
- Listen to the Action in an Effect using
ofType
- Switch to the api call using
switchMap
- Dispatch an Action
BananaActions.ReceiveBanana
with the result of the api call (hopefully a banana) - In case something goes wrong (i.e. the server is out of bananas) handle the error using
catchError
and return an Observable with an appropriate Action
This implementation bears a problem though: While BananaActions.OutOfBananas
will be dispatched if the api call throws an error, the Effect will also complete as we return a new Observable using of()
. This is not optimal as an Effect should not complete during runtime. It is supposed to be a hot Observable. If an Effect completes it stops handling new Action emissions and is basically useless afterwards.
To prevent this behavior we have to adapt the catchError
statement slightly:
1 | catchError((error, source) => source.pipe(startWith(BananaActions.OutOfBananas({ error })))); |
Instead of returning a new Observable we use the second parameter of catchError
which is a reference to the original Observable. Using startWith
we return our desired error Action and thus “revive” the Effect. (thanks to cartant on GitHub for this tip).
Finally we can encapsulate this into an own custom operator:
1 | export const catchSwitchMapError = |
With this operator the original code now looks only slightly different, but we magically avoid the problem of the “dying” Effect!
1 | requestBananaEffect = createEffect(() => |