0
votes

I'm trying to create an epic that will take an action, and then dispatch two different actions, with the second one delayed by two seconds. After a bunch of attempts, this was the best I could do:

const succeedEpic = action$ =>
  action$.filter(action => action.type === 'FETCH_WILL_SUCCEED')
    .mapTo({ type: 'FETCH_REQUEST' })
    .merge(Observable.of({ type: 'FETCH_SUCCESS' }).delay(2000))

Unfortunately, it seems that:

Observable.of({ type: 'FETCH_SUCCESS' }).delay(2000)

Is run immediately upon my app being loaded (rather than when an event comes down the parent stream). I noticed this because the FETCH_SUCCESS action is received by the reducer exactly two seconds after my app is loaded. I even attached a console.log to confirm this:

const succeedEpic = action$ =>
  action$.filter(action => action.type === 'FETCH_WILL_SUCCEED')
    .mapTo({ type: 'FETCH_REQUEST' })
    .merge(Observable.of({ type: 'FETCH_SUCCESS' })
           .do(() => console.log('this has begun'))
           .delay(2000)
          )

"this has begun" is logged to the console the moment the app is started.

I suspect this has something to do with how Redux-Observable automatically subscribes for you.

The desired behaviour is that I will:

  1. Click a button that dispatches the FETCH_WILL_SUCCEED event.
  2. Immediately, a FETCH_REQUEST event is dispatched.
  3. Two seconds after that, a FETCH_SUCCESS event is dispatched.
2

2 Answers

2
votes

It turns out I needed to wrap both of my events inside a mergeMap. Thanks to @dorus on the RxJS Gitter channel for this answer.

This is my working result:

const succeedEpic = action$ =>
  action$.filter(action => action.type === 'FETCH_WILL_SUCCEED')
    .mergeMapTo(Observable.of({ type: 'FETCH_REQUEST' })
      .concat(Observable.of({ type: 'FETCH_SUCCESS' })
        .delay(1000)))

merge should work as well in place of concat, but I thought concat makes better semantic sense.

0
votes

There might be a more elegant solution, but why not just use 2 epics and combine them?

The first one dispatches the fetch request:

const onFetchWillSucceed = action$ => action$.ofType('FETCH_WILL_SUCCEED')
  .mapTo({ type: 'FETCH_REQUEST' })

The second one waits 2 secs and dispatches the success:

const onFetchRequest = action$ => action$.ofType('FETCH_REQUEST')
  .delay(2000)
  .mapTo({ type: 'FETCH_SUCCESS' })

And in the end they are just combined into 1 epic

const both = combineEpics(onFetchWillSucceed, onFetchRequest)