меню

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 которые также позволят "убить"" последовательность, соотвественно не прийдется от нее отписываться.


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

Angular Signals

Версия Angular 16 была выпущена в начале мая, и эта версия полна новых функций. Но одной из особенностей, о которой все говорят, является новая возможность - Сигналы. Это настоящий поворотный момент, который находится в стадии предварительного просмотра разработчиками и будет выпущен позже в этом году, в версии 17.

Директива ngFor

Вывод данных в цикле с помощью директивы ngFor

Навигация. Передача данных событиями

Навигация в Angular на основе событий. Передача данных между вложенными компонентами с помощью событий в Angular

Route Resolving

Разрешение данных это процесс их получения перед активацией роута. В приложении возможна задержка перед тем, как данные будут предоставлены сервером.

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

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

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

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

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

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

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