меню

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


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

Расширенные операторы RxJs, которые вы знаете, но недостаточно хорошо

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

Route Resolving

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

События и жизненный цикл

Жизненный цикл приложения и события в Angular

Route. Переключения и параметры

Работа с роутером и ссылками в Angular. Программное переключение роутера и параметры пути в Angular

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

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

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

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

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

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

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