Никогда больше не пишите 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.
Хорошего дня, и до скорой встречи!
Ссылки
- Библиотека angular-ultils npm
- Репозиторий angular-utils GitHub