Rexam
Rexam

Reputation: 913

How to prevent NgRx effects after dispatching an action?

I'm facing a very weird bug in my Angular application. I have a Logout button which, when pressed, dispatches some actions to set parameters in the store that are no longer necessary to undefined. I also have a component called Storage which (among other stuffs) grabs pieces of state in its NgOnInit() method. The structure of the method is the following:

    this.store.pipe(select(userProfile)).pipe(
      switchMap( user => {
          if (!currentUser) {
            throw new Error('User must be logged in');
          }
         // ... do something ...
          return this.store.pipe(select(userResources));
        }
      ),
      switchMap(
        response => {
          // ... more operations ...
          return ...
        }
      )
    ).subscribe(
      response => {
        console.log("GOT RESPONSE");
      },
      (error: HttpErrorResponse) => {
        console.log(error);
        console.log("GOT ERROR");
      }
    );

Every time I display this component and this method is called, everything works perfectly, I retrieve the pieces of state that I need and I show the information to the user. What is weird instead is that anytime I pass by this component (e.g. I go to the Storage page and then go back to the homepage), if I press the Logout button after, the error "User must be logged in" is thrown as the store.pipe would be executed again even if I'm no more in the page that displays that component!! The ngOnInit() itself is not re-executed or re-called (I'm not in that component after all when I press Logout, I just passed by) but that portion of code gets executed and I have no idea why.

Also, the portion of code I mentioned seems to be executed for the number of times I passed by the Storage page (e.g. if I pass three times by the Storage page, I get the "GOT ERROR" message three times after).

The Logout button does something like that instead:

    this.authenticationService.logout();
    this.router.navigateByUrl(`/`);
    this.resourceStore.dispatch(new Logout());
    this.store.dispatch(new Logout());

Any idea? Is there here some sort of ripple effect I am not aware of?

Upvotes: 1

Views: 2046

Answers (1)

Andrew Allen
Andrew Allen

Reputation: 8002

Subscriptions exist beyond the lifetime of the component. A common memory leak is to forget to unsubscribe your subscription when the component is destroyed via the ngOnDestroy lifecycle hook.

Also selectors fire any time relevant state changes hence the repeated executions

A fuller descussion can be found here

You can fix your code by adding the following:

takeUntil method

  private destroyed$ = new Subject<void>()

  ngOnDestroy() {
    this.destroyed$.next()
    this.destroyed$.complete()
  }
    this.store.pipe(select(userProfile)).pipe(
      takeUntil(this.destroyed$),
    ... rest of RxJS stream and subscription
    );

Upvotes: 1

Related Questions