Как использовать BehaviorSubject для хранения и обработки пользовательских значений
Что проверяют
Понимаете ли вы, чем BehaviorSubject отличается от обычного Subject, и умеете ли строить на нём сервис состояния, который нельзя сломать снаружи. Следом обычно спрашивают про отписку и про то, не проще ли сейчас взять сигналы Angular.
Как отвечать
Начните с двух свойств: BehaviorSubject требует начальное значение и отдаёт текущее каждому новому подписчику сразу при подписке. Поэтому он подходит для состояния, а Subject для событий, которые важны только в момент возникновения.
Каркас сервиса:
- приватный субъект:
private readonly user$ = new BehaviorSubject<User | null>(null), наружу толькоuser$.asObservable(). Менять значение может лишь сам сервис через свои методы; - иммутабельное обновление:
this.state$.next({ ...this.state$.value, name }). Новая ссылка нужна, чтобы сработалиdistinctUntilChangedи компоненты сOnPush; - обработка ввода: поток из поля формы пропускают через
debounceTime(300),distinctUntilChanged()и, если нужен запрос,switchMap, который отменит устаревший; - чтение в шаблоне: через
asyncpipe, он сам отпишется. В коде компонента помогаетtakeUntilDestroyed()из@angular/core/rxjs-interop; - текущее значение: свойство
valueилиgetValue()для синхронного чтения внутри сервиса, но не как замена подписке.
Хорошо добавить сравнение: ReplaySubject(1) тоже отдаёт последнее значение, но не требует начального и ничего не выдаёт, пока значения не было. И отдельно скажите про сигналы: в современных Angular-проектах простое синхронное состояние всё чаще держат в signal, а RxJS оставляют для асинхронных потоков. Мост между ними дают toSignal и toObservable.
Где спотыкаются
Делают субъект публичным. Тогда next() вызывает любой компонент, и найти, кто испортил состояние, уже трудно.
Меняют объект на месте и передают в next() ту же ссылку. distinctUntilChanged считает, что ничего не поменялось, и интерфейс не обновляется.
Подписываются в ngOnInit вручную и не отписываются. Сервис живёт дольше компонента, и каждый заход на экран добавляет ещё одну подписку.