меню

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. Его компонентная архитектура позволяет разработчику разделить приложение на небольшие многократно используемые фрагменты.

Директива атрибута

Создание директивы атрибута и использование класса Renderer2 в Angular

Route. Вынос настроек роутера

Пример переноса роутинговой конфигурации Angular5 в отдельный файл

Route Resolving

Разрешение динамических данных перед активацией роута и интерфейс Resolve в Angular

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

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

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

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

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

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

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