меню

Angular & Rxjs: Отписываться или не отписываться?

Вы наверное уже знаете при подписке на обозреваемую последовательность либо просто событие в Javascript вы обычно должны в определенный момент отписываться, чтобы освободить память. Иначе это приведет к утечке памяти.

Мы рассмотрим основные случаи, когда вы должны отписываться в ngOnDestroy хуке компонента, а также случаи, когда можно не отписываться.

Angular & Rxjs, когда нужно отписываться

Формы

Необходимо отписываться от формы и от отдельных формконтролов, на которые подписались:

  
export class TestComponent {

  ngOnInit() {
    this.form = new FormGroup({...});
    this.valueChangesSubscription  = this.form.valueChanges.subscribe(console.log);
    this.statusChangesSubscription = this.form.statusChanges.subscribe(console.log);
  }

  ngOnDestroy() {
    this.valueChangesSubscription.unsubscribe();
    this.statusChangesSubscription.unsubscribe();
  }
}
 

Роутер

Согласно официальной документации Angular должен сам отписывать Вас, но этого не происходит, поэтому:

  
export class TestComponent {
  constructor(private route: ActivatedRoute, private router: Router) { }

  ngOnInit() {
    this.route.params.subscribe(console.log);
    this.route.queryParams.subscribe(console.log);
    this.route.fragment.subscribe(console.log);
    this.route.data.subscribe(console.log);
    this.route.url.subscribe(console.log);
    
    this.router.events.subscribe(console.log);
  }

  ngOnDestroy() {
    // You should unsubscribe from each observable here
  }

}
  

Рендерер

 
export class TestComponent {
constructor(private renderer: Renderer2, 
            private element : ElementRef) { }

  ngOnInit() {
    this.clickSubscription = this.renderer.listen(this.element.nativeElement, "click", handler);
  }

  ngOnDestroy() {
    this.clickSubscription.unsubscribe();
  }

}
 

Бесконечные обозреваемые последовательности

Примерами бесконечных могут служить последовательности созданные с помощью interva() или слушающие события fromEvent():

  
export class TestComponent {

  constructor(private element : ElementRef) { }

  interval: Subscription;
  click: Subscription;

  ngOnInit() {
    this.intervalSubscription = Observable.interval(1000).subscribe(console.log);
    this.clickSubscription = Observable.fromEvent(this.element.nativeElement, 'click').subscribe(console.log);
  }

  ngOnDestroy() {
    this.intervalSubscription.unsubscribe();
    this.clickSubscription.unsubscribe();
  }

}
  

ngRx

От подписок на состояние Store ngRx тоже необходимо отписываться:

  
export class TestComponent {

  constructor(private store: Store) { }

  todos: Subscription;

  ngOnInit() {
     this.todosSubscription = this.store.select('todos').subscribe(console.log);  
  }

  ngOnDestroy() {
    this.todosSubscription.unsubscribe();
  }

}
  

Angular & Rxjs, когда не нужно отписываться

Async pipe

C async pipe нам повезло и он выполнят работу под отписке за нас:

  
@Component({
  selector: 'test',
  template: `<todos [todos]="todos$ | async"></todos>`
})
export class TestComponent {

  constructor(private store: Store) { }

  ngOnInit() {
     this.todos$ = this.store.select('todos');
  }

}
  

@HostListener

Так же нам не нужно отписываться, когда мы навешиваем слушатель события с помощью HostListener:

 
export class TestDirective {

  @HostListener('click')
  onClick() {
    ....
  }


}
 

Конечные обозреваемые последовательности

Бывают последовательности, которые сами завершаются, такие как HTTP и timer:

  
export class TestComponent {

  constructor(private http: Http) { }

  ngOnInit() {
    Observable.timer(1000).subscribe(console.log);
    this.http.get('http://api.com').subscribe(console.log);
  }


}
  

Final tip

Также вы можете использовать оператор takeUntil, который позволит писать код в декларативном стиле, не отписываясь от каждого обозревателя отдельно:

 
export class TestComponent {

  constructor(private store: Store) { }

  private componetDestroyed: Subject = new Subject();
  todos: Subscription;
  posts: Subscription;

  ngOnInit() {
     this.todosSubscription = this.store.select('todos').takeUntil(this.componetDestroyed).subscribe(console.log); 

     this.postsSubscription = this.store.select('posts').takeUntil(this.componetDestroyed).subscribe(console.log); 
  }

  ngOnDestroy() {
    this.componetDestroyed.next();
    this.componetDestroyed.complete();
  }

}
  

Кроме takeUntil вы еще можете использовать take, takeWhile и first которые также позволят "убить"" последовательность, соотвественно не прийдется от нее отписываться.


Возможно, вам будет интересно

10 лучших UI библиотек для Angular

Angular зарекомендовал себя, как один из самых популярных фреймворков JavaScript. Его компонентная архитектура позволяет разработчику разделить приложение на небольшие многократно используемые фрагменты.

Route передача статичных данных

Как передать статичные данные в компонент с помощью настроек роута.

Обнаружение локальных изменений в Angular 17

Как использовать сигналы Angular и стратегию обнаружения изменений OnPush для улучшения производительности с локальным обнаружением изменений.

Появление нового функционала в Angular 16 и Angular 17

Angular предоставляет новые возможности для замены директив ngFor и ngIf. Все, что вам нужно сделать, это заиспользовать @for и @if, как вы используете директивы ngFor и ngIf, но с немного другим синтаксисом.

Оформление заявки

Документы на создание сайта

Изучите наше коммерческое предложение, заполните БРИФ и отправьте его на почту maxidebox@list.ru. Изучив все пожелания из БРИФ-а, обратным ответом оповестим Вас по стоимости разработке, ответим на вопросы.

КП на создание сайта Коммерческое предложение на созданеи сайта

Мы берем на себя ответственность за все стадии работы и полностью избавляем клиентов от забот и необходимости вникать в тонкости.

Скачать БРИФ (акета) на создание сайта Скачать БРИФ (акета) на создание сайта

Зополните у БРИФ-а все необходимые поля. Сделайте краткое описание к каждому из пунктов анкеты, привидите примеры в соответсвующий пунктах - это позволит лучше понять Ваши ожидания и требования к сайту