0
votes

I am using Redux-Observable Epic in a React & Redux project. I have multiple actions need to emit, originally this is what I have been doing,

1) Catch the START_ACTION in the Epic
2) Fetch remote data
3) Return a new Observanble

for example:

import fetch from 'isomorphic-fetch';
import {Observable} from 'rxjs/Observable';
import {switchMap} from 'rxjs/operator/switchMap';
import {mergeMap} from 'rxjs/operator/mergeMap';


const fetchAsync = (arg) => {
   // return an observable of promise
   return Observable::from(fetch(url + arg));
} 

export function myEpic = (action$) => {
   action$.ofType(START_ACTION)
    ::switchMap(action => {
       return fetchAsync('/action.payload')
          ::mergeMap(result => {
             return Observable::of({type: ACTION_COMPLETE, payload: result})
          }) 
    })
 };

Now what if I have another action SECOND_ACTION need to be emitted after the START_ACTION and before the ACTION_COMPLETE ? In other word, without making sure the SECOND_ACTION hits the reducer, the ACTION_COMPLETE should not be emitted.

I could write another separate Epic function to do this, but is there any other easier way?

1
What should happen to the ACTION_COMPLETE action while it waits for the SECOND_ACTION? What if it never comes?jayphelps
Or do you mean you want this epic to emit SECOND_ACTION then immediately followed by the ACTION_COMPLETE? i.e. emit two actionsjayphelps
I want to emit FIRST_ACTION then immediately followed by SECOND_ACTION. I am thinking about using dispatch(SECOND_ACTION) in the action function which return FIRST_ACTIONJun Q
Where is FIRST_ACTION coming from? It's not in your question above. did you maybe mean ACTION_COMPLETE?jayphelps

1 Answers

1
votes

To simplify the question, I just want to emit the SECOND_ACTION before the async.

To emit another action before performing the fetchAsync, you can either use Observable.concat or the startWith operator, which is basically sugar for the concat.

export function myEpic = (action$) => {
   action$.ofType(START_ACTION)
    ::switchMap(action => {
       return fetchAsync('/action.payload')
          ::map(result => ({ type: ACTION_COMPLETE, payload: result })
          ::startWith({ type: SECOND_ACTION })
    })
 };

Since SECOND_ACTION synchronously follows START_ACTION, keep in mind that often you should just have your reducers listen for START_ACTION instead of emitting another action. Multiple reducers transitions state from the same action is normal and one of the primary benefits of redux.

That said, there are certainly some times where the separation of concerns is more ideal, so this is more of a general tip.


Previous answer

If you want to emit two actions sequentially you can pass the additional actions as arguments to Observable.of(...actions) since it accepts any number of arguments and emits them sequentially.

export function myEpic = (action$) => {
   action$.ofType(START_ACTION)
    ::switchMap(action => {
       return fetchAsync('/action.payload')
          ::mergeMap(result => {
             return Observable::of(
               { type: SECOND_ACTION },
               { type: ACTION_COMPLETE, payload: result }
             )
          }) 
    })
 };

If this isn't what you meant, I apologize. The question isn't clear.