bigpotato
bigpotato

Reputation: 27527

Redux Observable: How to use takeUntil inside flatMap?

I have a flatMap with the purpose of emitting two actions:

export default function searchForPartner(action$, store) {
  return action$.ofType(statusActions.SEARCH_FOR_PARTNER)
    .filter(() => store.getState().user.signedIn)
    .delay(2000)
    .flatMap(() => [
      carouselActions.slideTo(config.CAROUSEL_SLIDES.CONFIRM_CHAT),
      statusActions.foundPerson(),
    ])
    .takeUntil(action$.ofType(carouselActions.TAP_CANCEL))
};

However, the takeUntil is in the wrong spot. It is cancelling the whole epic when TAP_CANCEL is emitted. Instead, I only want it to cancel the emitting of the actions inside flatMap. How would I do this?

=== UPDATE ===

I fooled around with it and this seems to work? But I don't like that there are 2 timers:

export default function searchForPartner(action$, store) {
  return action$.ofType(statusActions.SEARCH_FOR_PARTNER)
    .filter(() => store.getState().user.signedIn)
    .mergeMap(() =>
      Observable.merge(
        Observable.timer(2000)
          .map(() => carouselActions.slideTo(config.CAROUSEL_SLIDES.CONFIRM_CHAT)),
        Observable.timer(2000)
          .map(() => statusActions.foundPerson())
      )
      .takeUntil(action$.ofType(carouselActions.TAP_CANCEL))
    )
};

Upvotes: 2

Views: 2539

Answers (1)

jayphelps
jayphelps

Reputation: 15401

You'll want to isolate the delay and the takeUntil into the inner Observable, the most important thing being that you start the delay timer, then takeUntil it, so that if someone cancels it, you are for sure listening--if your delay was outside, you wouldn't be listening for the cancel action yet! This is subtle, but important to understand why.

export default function searchForPartner(action$, store) {
  return action$.ofType(statusActions.SEARCH_FOR_PARTNER)
    .filter(() => store.getState().user.signedIn)
    .flatMap(() =>
      Observable.of(
        carouselActions.slideTo(config.CAROUSEL_SLIDES.CONFIRM_CHAT),
        statusActions.foundPerson()
      )
        .delay(2000)
        .takeUntil(action$.ofType(carouselActions.TAP_CANCEL))
    );
}

You may also want to consider using switchMap instead of flatMap (aka mergeMap) because if another SEARCH_FOR_PARTNER comes in while the previous is still waiting, you'll have a queue of them lined up. Probably not want you want, but you'll have to make that call.

Upvotes: 5

Related Questions