user1312242
user1312242

Reputation: 337

Angular Subscribers - auto unsubscribe a subscriber when the component destroys

I have a large application built on Angular 7. I want to implement something which will unsubscribe the RxJs subscribers if developer has forgotten to unsubscribe in ngOnDestroy. This is to ensure no memory leak in application.

Is it possible with Guards? When the route changes, the Guard will check which component was last loaded and unsubscribe its subscribers?

Upvotes: 0

Views: 485

Answers (4)

Patrick
Patrick

Reputation: 1291

The recommended way to do this is to use the takeUntil operator together with ngOnDestroy. For example:

    import { Component, OnDestroy, OnInit } from '@angular/core';
    // RxJs 6.x+ import paths
    import { filter, startWith, takeUntil } from 'rxjs/operators';
    import { Subject } from 'rxjs';
    import { BookService } from '../books.service';
    
    @Component({
        selector: 'app-books',
        templateUrl: './books.component.html'
    })
    export class BooksComponent implements OnDestroy, OnInit {
        private ngUnsubscribe = new Subject<void>();
    
        constructor(private booksService: BookService) { }
    
        ngOnInit() {
            this.booksService.getBooks()
                .pipe(
                   startWith([]),
                   filter(books => books.length > 0),
                   takeUntil(this.ngUnsubscribe)
                )
                .subscribe(books => console.log(books));
    
            this.booksService.getArchivedBooks()
                .pipe(takeUntil(this.ngUnsubscribe))
                .subscribe(archivedBooks => console.log(archivedBooks));
        }
    
        ngOnDestroy() {
            this.ngUnsubscribe.next();
            this.ngUnsubscribe.complete();
        }
    }

For more details, see this answer: Angular/RxJS When should I unsubscribe from `Subscription`

You might also be interested in these tslint rules. They will ensure some level of correct use of RxJS.

Upvotes: 1

Serhii
Serhii

Reputation: 51

  1. Create new file auto-unsubscribe.ts containing exported class autoUnsubscribe

@Injectable()
export class autoUnsubscribe {
   subscriptions_: any[] = []
   get subscriptions(): Subscription[] {
    return this.subscriptions_
  }
   set subscriptions(v: any) {
    this.subscriptions_.push(v)
  }
   ngOnDestroy(): void {
    this.subscriptions.forEach((s) => {
      s.unsubscribe()
    })
  }
}

  1. On every component declaration add extends statement

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent extends autoUnsubscribe {
  title = 'app';
}

  1. Anywhere in your component class you can execute Subscribe like this:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent extends autoUnsubscribe implements OnInit{
  title = 'app';
  
  
  ngOnInit() {
    this.subscriptions=interval(2000).subscribe(console.log)
  }
}

  1. Extended class will take care of the rest!

Upvotes: 0

MindingData
MindingData

Reputation: 12480

Is it possible with Guards? When the route changes, the Guard will check which component was last loaded and unsubscribe its subscribers?

Unfortunately not out of the box. And the reason is it would be hard to discern which list of subscriptions you should be unsubscribing from. For example, it could be any of these :

subscriptions : Subscription[]
subscriptions : any[]
subscriptions : any

Or even subscriptions inside a service that you want to unsubscribe.

However there are libraries that will do some of this for you (For example : https://tutorialsforangular.com/2020/12/14/auto-unsubscribing-from-observables-on-ngdestroy/). Unfortunately they still require either an attribute on the component, and/or some conventions to be used that your subscriptions array is always named the same.

It may also be possible (Since it's open source), to take a dive into the code and be able to apply it to a routeguard etc.

Upvotes: 1

Melvin Kah Hoo
Melvin Kah Hoo

Reputation: 196

The easiest way i can think of is to use AsyncPipe on your application

This way angular will unsubscribe the observable when the component is destroy

Ref: https://angular.io/api/common/AsyncPipe

Upvotes: 2

Related Questions