В моем проекте Angular я пытаюсь получить значения нескольких селекторов ngrx и использовать их все для создания строки запроса, которая будет использоваться для вызова API.
Я хочу подождать, пока все они не выдадут ненулевое значение, а затем выдадут только один раз (чтобы вызов API не отправлялся несколько раз, если значения меняются со временем). Это решение, которое я получил:
const selectorA$ = this.store.select(...)
const selectorB$ = this.store.select(...)
const selectorC$ = this.store.select(...)
return combineLates([selectorA$, selectorB$, selectorC$]).pipe(
first(list => list.every(item => item != null)),
map(([selectorA, selectorB, selectorC]) => //... return query string using the store values)
)
Мне интересно, правильный ли это подход или есть ли встроенный оператор rxjs, который может дать мне такое поведение?
Спасибо





Вы можете просто использовать joinLatest с фильтром, чтобы поток продолжался только тогда, когда присутствуют все три значения!
Если вам нужен только первый экземпляр, мы можем использовать takeUntil для завершения потока, когда будет отправлен первый действительный ввод!
private destroy$ = new Subject<void>();
const selectorA$ = this.store.select(...)
const selectorB$ = this.store.select(...)
const selectorC$ = this.store.select(...)
return combineLatest([selectorA$, selectorB$, selectorC$]).pipe(
filter(list => list.every(item => item != null)),
takeUntil(this.destroy$)
map(([selectorA, selectorB, selectorC]) => {
// since we want only the first instance of valid input, we complete the subject, so that the stream is completed and no other events are taken! we use takeUntil for this!
this.destroy$.next();
this.destroy$.complete();
})
)
@ maxime1992, поскольку требованием вопроса является правильная комбинация всех трех наблюдаемых, не равная нулю, тогда только перестаньте прослушивать события! поэтому я использовал takeUntil
@AdiFuchs Этот ответ решает вашу проблему?
Возможно, это решится, но я изменил это, как сказал @maxime1992. поэтому сейчас я использую комбинированный селектор + first. надеюсь это хороший подход
@AdiFuchs, без проблем, спасибо, что уделили время!
@NarenMurali, просто чтобы ответить на твой последний ответ, но все равно все в порядке. combineLatest([selectorA$, selectorB$, selectorC$]).pipe(filter(list => list.every(item => item != null)), first()). Нет необходимости использовать внешнюю по отношению к потоку переменную класса, дублировать до тех пор, пока вручную не будет уничтожен/завершен объект на карте. Первый закроет поток, как только он получит значение, что происходит только после фильтра, что означает, что он будет действителен в этот момент.
Никогда не делайте этого:
const selectorA$ = this.store.select(...)
const selectorB$ = this.store.select(...)
const selectorC$ = this.store.select(...)
return combineLatest([selectorA$, selectorB$, selectorC$]).pipe(...)
Представьте, что вы отправили действие, которое влияет на состояние (один раз), и все ваши селекторы здесь полагаются на это состояние, и три из них обновляются (по-прежнему одновременно). Каждый раз, когда ваши селекторы обновляются, ваш combineLatest будет излучать (по крайней мере, после того, как все они были изначально отправлены, что в магазине ngrx произойдет сразу же, поскольку вы всегда получаете эмиссию, как только подписываетесь на состояние).
Таким образом, ваш combineLatest потенциально сработает 3 раза вместо одного (после инициализации), когда произойдет новое обновление состояния.
Как это решить? Создайте новую выборку, которая объединит три нужных значения. Он будет излучать только один раз, когда это необходимо.
export const abc = createSelector(selectorA, selectorB, selectorC, (a, b, c) => ({ a, b, c }))
И заменить
const selectorA$ = this.store.select(...)
const selectorB$ = this.store.select(...)
const selectorC$ = this.store.select(...)
return combineLatest([selectorA$, selectorB$, selectorC$]).pipe(...)
С
this.store.select(abc)
При использовании вашего подхода я по-прежнему получаю значения как неопределенные. Как я могу быть уверен, что использую их только тогда, когда они готовы?
Вы все равно можете добавить filter(list => list.every(item => item != null)), а затем first() или take(1).
Зачем нужна тема, takeUntil и карта с побочным эффектом вместо
first()илиtake(1)?