0
votes

I've read that normally you don't have to unsubscribe from Router or ActivatedRoute explicitly because:

The ActivatedRoute and its observables are insulated from the Router itself. The Router destroys a routed component when it is no longer needed and the injected ActivatedRoute dies with it.

source: Do I have to unsubscribe from ActivatedRoute (e.g. params) observables?

However I am calling subscribe() on ActivatedRoute.firstChild.paramMap repeatedly in the parent component app.component. This component is only destroyed when the user navigates away from the web application or closes the website so i'm worried that those paramMap subscriptions might remain active the entire time.

The subscription calls are made within a Router.events subscription and it looks like this:

this.routerSubscription = this.router.events
  .pipe(
    tap((event) => { 
      switch (true) {
        case event instanceof NavigationStart: { 
          setTimeout(() => this.showChildComponentLoading = true);
          break;
        }

        case event instanceof NavigationEnd:{
          this.activatedRoute.firstChild.paramMap.subscribe(paramMap => {
          if(paramMap.has('mode')){
              let newWebAppMode:string = paramMap.get('mode');
              if(this.dataStoreService.isValidWebAppMode(newWebAppMode) && newWebAppMode !== this.dataStoreService.currentAppMode)
                  this.saveToLocalStorage.next([DataType.WEB_APP_MODE, newWebAppMode]); 
          }
        });
//other code

Every time someone navigates to another component/page activatedRoute.firstChild.paramMap is subscribed to again. I have to do this inside the router subscription and only when the event is an instance of NavigationEnd because before that the url parameters are not yet available.

My question is, what happens to those subscriptions? Do they unsubscribe automatically or do i need to unsubscribe each new one manually? If the latter is the case, how can i do that effectively?

Some of you may advice me to use activatedRoute.firstChild.snapshot.paramMap.myParameter because then i don't have to subscribe to anything. However, snapshot does not work when the url parameter changes while the same component is reused. So i can't use that.

Thank you

2
when you navigate away, everything is cleared, nothing stays 'subscribed', because the entire js context is disposedPoul Kruijt

2 Answers

2
votes

Yes, it does look like a potential memory leak: on every NavigationEnd a new subscription is born, so that you have potentially unlimited number of identical observers of the same stream (and activatedRoute.firstChild.paramMap emits indefinitely). You can verify that by putting a simple console.log(new Date()) inside the subscription.

I believe switchMap is your friend here (it automatically unsubscribes from the old stream when subscribing to a new one). Sth like

this.router.events.pipe(
    filter(event => event instanceof NavigationEnd),
    switchMap(() => this.activatedRoute.firstChild.paramMap),
    filter(paramMap => paramMap.has('mode')),
    map(paramMap => paramMap.get('mode')),
    filter(newMode => this.dataStoreService.isValidWebAppMode(newMode) && 
        newMode !== this.dataStoreService.currentAppMode),
).subscribe(newMode => this.saveToLocalStorage.next([DataType.WEB_APP_MODE, newMode]);

BTW, don't you need to guard against this.activatedRoute.firstChild being null?

0
votes

No Need of unsubscribe in these cases

  1. In case of HttpClient calls because the observable emit one value (success or error) and complete automatically.

  2. In case or ActivatedRoute subscription because the Router will destroy it when the component is destroyed automatically

  3. use of Async pipe