Angular: Автоматическая отписка от наблюдаемых объектов при уничтожении

Никогда больше не пишите ngOnDestroy для очистки подписок.

Смотрите эту и многие другие статьи на сайте lucaspaganini.com

Одна вещь, которая действительно раздражает меня при работе с Angular – это необходимость сохранять все мои подписки, чтобы отписаться от них при ngOnDestroy.

Это так раздражает, что мне приходится делать это везде.
Я искал решения, но все они подразумевают объявление ngOnDestroy, а я этого не хочу.

Я просто хочу подписаться на что-то и сказать Angular автоматически отменить подписку, когда компонент будет уничтожен.

Однако я понял, что могу добиться желаемого, делегировав ngOnDestroy службе, что меня очень радует! Это была моя больная точка с момента выхода Angular 2.

Сервис в компоненте

Предоставляя сервис в компоненте, его существование становится зависимым от компонента.

Когда компонент уничтожается, уничтожается и сервис. Я покажу вам:

Предоставьте сервис в вашем компоненте, инжектируйте его в конструкторе и ведите лог, когда ngOnDestroy() происходит в сервисе и компоненте.

@Injectable()
export class UnsubscriberService implements OnDestroy {
  public ngOnDestroy(): void {
    console.log('service on destroy');
  }
}

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnDestroy {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnDestroy(): void {
    console.log('component on destroy');
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вы увидите, что служба уничтожается прямо перед компонентом.

Пока служба не уничтожится

Зная это, мы можем создать наблюдаемую переменную, которая испускает сигнал, когда служба уничтожается, и использовать takeUntil() для автоматической отписки, когда это произойдет.

@Injectable()
export class UnsubscriberService implements OnDestroy {
  private readonly _destroy$ = new Subject<void>();
  public readonly destroy$ = this._destroy$.asObservable();

  public ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
  }
}

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnInit {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnInit(): void {
    timer(0, 500)
      .pipe(takeUntil(this._unsubscriber.destroy$))
      .subscribe((x) => console.log(x));
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Для упрощения мы можем поместить логику takeUntil() в наш сервис и раскрыть ее через простой метод.

@Injectable()
export class UnsubscriberService implements OnDestroy {
  private readonly _destroy$ = new Subject<void>();

  public readonly takeUntilDestroy = <T>(
    origin: Observable<T>
  ): Observable<T> => origin.pipe(takeUntil(this._destroy$));

  public ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
  }
}

@Component({
  selector: 'app-test',
  template: 'test',
  providers: [UnsubscriberService]
})
export class TestComponent implements OnInit {
  constructor(private readonly _unsubscriber: UnsubscriberService) {}

  public ngOnInit(): void {
    timer(0, 500)
      .pipe(this._unsubscriber.takeUntilDestroy)
      .subscribe((x) => console.log(x));
  }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Я добавил это в свою библиотеку утилит Angular. Если вы не хотите ничего писать, вы можете просто установить библиотеку.

Заключение

Что вы думаете? Понравилось? Ненавижу? Есть лучшее решение? Дайте мне знать, что вы думаете, в моем Twitter.
Хорошего дня, и до скорой встречи!

Ссылки

  1. Библиотека angular-ultils npm
  2. Репозиторий angular-utils GitHub

Оцените статью
Procodings.ru
Добавить комментарий