OpherV
OpherV

Reputation: 6937

What alternatives are there to takeUntil with redux-observable and RxJS

I've started porting some async logic to redux-observables and I'm struggling a little with this.

My use case is this: I have a UI overlay that's supposed to open on an OPEN_OVERLAY action, and automatically close after three seconds (by emitting CLOSE_OVERLAY) UNLESS either an INTERRUPT_CLOSE_OVERLAY1 or INTERRUPT_CLOSE_OVERLAY2 action is received, in which case the overlay shouldn't close.

My first take was this:

export const epic = action$ => action$.pipe(
    filter(action => action.type === "OPEN_OVERLAY"), 
    delay(3000), 
    takeUntil(action$.ofType("INTERRUPT_CLOSE_OVERLAY1", "INTERRUPT_CLOSE_OVERLAY2")),
    mapTo({type: "CLOSE_OVERLAY})
);

Which works, but just once. That is, if you open the overlay and interrupt it, then after closing and opening it again it will never auto-close.

I understand that this happens because takeUntil effectively unsubscribes from the Observable. However the way I understand it with redux-observables you only define the epic once at setup and you're not supposed to be subscribing/unsubscribing.

So, how would I structure this that open/close/autoclose always works without unsubscribing?

Upvotes: 1

Views: 791

Answers (1)

Andrei Gătej
Andrei Gătej

Reputation: 11969

Here would be my approach:

actions$.pipe(
  filter(action => action.type === "OPEN_OVERLAY"), 
  switchMap(
    () => timer(3000)
      .pipe(
        mapTo({ type: "CLOSE_OVERLAY" }),
        takeUntil(action$.ofType("INTERRUPT_CLOSE_OVERLAY1", "INTERRUPT_CLOSE_OVERLAY2")),
      )
  )
)

It starts the timer when "OPEN_OVERLAY" is emitted, then if the observable provided to takeUntil does not emit in the meanwhile, it will successfully emit "CLOSE_OVERLAY". Otherwise, the inner observable will complete, so there is no way it can emit unless "OPEN_OVERLAY" restarts it.

Upvotes: 2

Related Questions