I have a use case where I need to wait for a sequence of actions before I dispatch another using Redux Observables. I've seen some similar questions but I cannot fathom how I can use these approaches for my given use case.
In essence I want to do something like so:
action$
.ofType(PAGINATION_CLICKED) // This action occurred.
.ofType(FETCH_SUCCESS) // Then this action occurred after.
.map(() => analyticsAction()); // Dispatch analytics.
I would also like to cancel and start that sequence over again if another action of type FETCH_ERROR
fires for example.
Great question. The important point is that
action$
is a hot/multicast stream of all actions as they are dispatched (it's a Subject). Since it's hot we can combine it multiple times and they'll all be listening to the same stream of actions.So every time we receive
PAGINATION_CLICKED
we'll start listening to that inner Observable chain that listens for a singleFETCH_SUCCESS
. It's important to have that.take(1)
because otherwise we'd continue to listen for more than oneFETCH_SUCCESS
which might cause strange bugs and even if not is just generally best practice to only take what you need.We use
takeUntil
to cancel waiting forFETCH_SUCCESS
if we receiveFETCH_ERROR
first.As a bonus, if you decide you want also to do some analytics stuff based on the error too, not only start over, you can use
race
to indeed race between the two streams. First one to emit, wins; the other is unsubscribed.Here's the same thing, but using
race
as an instance operator instead of the static one. This is a stylistic preference you can choose. They both do the same thing. Use whichever one is more clear to you.