Reputation: 41
I have two actions in a scenario:
INITIATE
which triggers on a button click, and FETCH_SUCCESS
which triggers when data has been stored in the state from an AJAX request called on page load.
The epic handling the INITIATE
action requires the data from FETCH_SUCCESS
being available in the state to work.
How can I write this epic so that it will 1) Do its logic as normal, as many times as it's been clicked after FETCH_SUCCESS
has ran once, and 2) If clicked before FETCH_SUCCESS
, wait for FETCH_SUCCESS
to occur, and then continue to logic once the data is available / the success action has been ran.
There are at least a few easy approaches that solves this problem that I can think of, but don't want if this is possible to achieve with a single rxjx epic. For example: 1) Fail/retry flow where the epic simply checks if the data is available, and if not recursively calls itself with a small delay. 2) Make INITIATE
set a flag in the state if FETCH_SUCCESS
hasn't ran yet, and then listen to FETCH_SUCCESS
and make it kick off INITIATE
if this flag is set when it runs.
The only way I've come up with that don't include those two methods are writing it in two epics:
The version to handle FETCH_SUCCESS
before INITIATE
(happy path) looks like this:
export const initiateFirstEpic = action$ => action$
.ofType(INITIATE)
.skipUntil(Observable.of(FETCH_SUCCESS))
.map(action => {
return LogicAction();
});
And the other to handle clicking too fast, making INITIATE
happen before FETCH_SUCCESS
:
export const successFirstEpic = action$ => action$
.ofType(INITIATE)
.switchMap(() =>
action$.ofType(FETCH_SUCCESS)
.take(1)
.map(action => {
return LogicAction();
})
);
This doesn't seem to work, nor does it really look the way I want it to. Is there a good way to achieve this? I am quite new to RxJS and Redux Observable, so the answer might be obvious.
Upvotes: 2
Views: 1642
Reputation: 1202
Here is a version based on @m1ch4ls but using the operator instead:
const initiateEpic = action$ =>
action$
.combineLatest(action$.ofType(FETCH_SUCCESS).take(1))
.map(([initiateAction, fetch_success_action]) => {
return LogicAction();
});
Originally I didn't grasp that there was a static function Observable.combineLatest(obs1$, obs2$)
and an operator obs1$.combineLatest(obs2$)
, hence my comment to @m1ch4ls answer.
Upvotes: 0
Reputation: 3435
I think what you are looking for is combineLatest.
export const initiateEpic = action$ => Observable
.combineLatest(action$.ofType(INITIATE), action$.ofType(FETCH_SUCCESS).take(1))
.map(([action, _]) => {
return LogicAction();
});
Optionally use take(1)
on FETCH_SUCCESS
action ofType
observable to prevent producing the LoginAction
every time you get FETCH_SUCCESS
Upvotes: 1