Search code examples
angularrxjsngrx

RXJS chain subscriptions anti-pattern with NGRX


I have the following code, which is working, but I've read that chaining subscriptions is an anti-pattern. I need to make sure that the user has a loggedin state of true, before making a call to the API which requires the users authtoken.

What would be the proper way to handle this in RXJS?

const subscrAuth = this.store
  .pipe(select(isLoggedIn))
  .subscribe(isLoggedIn => {
    console.log(isLoggedIn);
    if (isLoggedIn) {
      const subscrLoaded = this.store
        .pipe(select(hasCarriersLoaded))
        .subscribe(hasCarrierLoaded => {
          if (!hasCarrierLoaded) {
            this.store.dispatch(new CarriersPageRequested());
          }
        });
    }
  });
this.unsubscribe.push(subscrAuth);

Solution

  • If it's just an API call, you wouldn't need to unsubscribe, because it completes by itself. You can use switchMap to chain and filter to.. filter :). And you have a nice chain of rxjs operators, which self completes

    this.store.pipe(
      select(isLoggedIn),
      take(1),
      filter((isLoggedIn) => isLoggedIn),
      switchMapTo(this.store),
      select(hasCarriersLoaded),
      take(1),
      filter((hasCarriersLoaded) => !hasCarriersLoaded),
    ).subscribe(() => this.store.dispatch(new CarriersPageRequested()));
    

    It feels like though that you can combine the two selects:

    combineLatest([
      this.store.pipe(select(isLoggedIn)),
      this.store.pipe(select(hasCarriersLoaded))
    ]).pipe(
      take(1),
      filter(([loggedIn, hasCarriers]) => loggedIn && !hasCarriers)
    ).subscribe(() => this.store.dispatch(new CarriersPageRequested()));
    

    If you however need to wait before the user is logged in, while handling this request, you can do the following:

    combineLatest([
      this.store.pipe(select(hasCarriersLoaded)),
      this.store.pipe(
        select(isLoggedIn),
        filter((loggedIn) => loggedIn)
      )
    ]).pipe(
      take(1),
      filter(([hasCarriers]) => !hasCarriers)
    ).subscribe(() => this.store.dispatch(new CarriersPageRequested()));
    

    If you also need to wait to be logged in to call hasCarriersLoaded, you should switch the filter with the take(1) in the first example