Как использовать withLatestFrom с селектором в ngrx? - PullRequest
1 голос
/ 18 февраля 2020

У меня есть приложение ngrx, и я не могу получить значение из селектора в результате по полезной нагрузке, которую я отправил в компоненте.

Я пишу пример кода того, с чем я имею дело:

Это мое состояние, в нем простой массив документов.

export const initialState = {
  docs: [{ id: 1, name: "doc1" }, { id: 2, name: "doc2" }]
};

export const reducer = createReducer(
  initialState,
);

Я не выполняю никаких действий по этим вопросам, это не проблема.

В моем компоненте Я получаю массив с помощью селектора:

docs$ = this.store.pipe(select(fromStore.getAllDocs));

{{docs$ | async | json}}

редуктор:

export const selectDocsState = createFeatureSelector("docs");

export const selectDocsStatusState = createSelector(
  selectDocsState,
  state => state["docs"]
);

export const getAllDocs = createSelector(
  selectDocsStatusState,
  getDocs
)

У меня также есть селектор, чтобы получить по id:

{{docsById$ | async | json }}

docsById$ = this.store.pipe(select(fromStore.getById, { id: 1 }));

в редуктор:

export const getById = createSelector(
  selectDocsStatusState,
  getDocs,
  (state, docs, props) => {
    console.log({ props });
    return docs.docs.docs.filter(d => d.id === props.id);
  }
)

Пока все хорошо. Я получаю массив документов и отображаю его.

Теперь, в моем компоненте я отправляю некоторые действия и ловлю по эффекту:

this.store.dispatch({ type: 'GET_DOC', payload: { id: 2 } });

И в результате я хочу знать, является ли этот идентификатор в магазине или нет.

Поэтому я использую withLatestFrom из rx js с селектором getById.

withLatestFrom((a: any) =>
  this.store.pipe(select(fromDocs.getById, { id: a.payload.id }))
),

Проблема в том, что я не получаю значение от селектора, а получаю экземпляр хранилища.

tap(v => {
  console.log({ v });
}),

Полный эффект:

getDoc$ = createEffect(() =>
    this.actions$.pipe(
      ofType("GET_DOC"),
      map(a => a),
      tap(a => {
        console.log({ a });
      }),
      withLatestFrom((a: any) =>
        this.store.pipe(select(fromDocs.getById, { id: a.payload.id }))
      ),
      tap(v => {
        console.log({ v }); // <--------------- HERE SHOULD BE THE DOC AND THE ACTION.PAYLOAD, v[0].a.payload, v[1].id
      }),
      exhaustMap(payload => {
        return of({ type: "SOME_ACTION", payload: null });
      })
    )
  );

Вот мой полный пример в stackbliz.

Что мне здесь не хватает?

Ответы [ 2 ]

3 голосов
/ 18 февраля 2020

Это описано в Документах NgRx - включающее государство , но я нашел Комментарий Брэндона Робертса github более полезный:


Есть два Преобладающие действия, которые вы делаете при прослушивании действия после использования оператора ofType:

actions.pipe(
  ofType('SOME_ACTION')
  someMap(action => doSomething(action))
)

и

actions.pipe(
  ofType('SOME_ACTION'),
  withLatestFrom(store.select(someThing)),
  someMap(([action, latest]) => doSomething(action, latest))
)

Они имеют два различных поведения в отношении эффектов. Первый ничего не делает, пока не отправлено действие, что и должно быть сделано.

Второй немедленно подписывается на withLatestFrom независимо от того, отправлено действие или нет. Если это состояние, используемое селектором, еще не инициализировано, вы можете получить ошибку, которую не ожидаете. Чтобы сделать его «ленивым», вам в конечном итоге придется вложить его в уплощаемый наблюдаемый оператор.

actions.pipe(
  ofType('SOME_ACTION'),
  someMap(action =>
    of(action).pipe(
      withLatestFrom(store.select(someThing)),
      someMap(([action, latest]) => doSomething(action, latest))
    )
)

Применение этого дает следующее:

  getDoc$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(fromDocs.getDocument),
      switchMap(action =>
        of(action).pipe(
          withLatestFrom(
            this.store.select(fromDocs.getById, { id: action.payload.id })
          ),
          map(([action, latest]) => {
            return fromDocs.someAction();
          })
        )
      )
    );
  });

Stackblitz

0 голосов
/ 18 февраля 2020

withLatestFrom не принимает составную функцию, он требует вторичного наблюдаемого источника.

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

Попробуйте:

getDoc$ = createEffect(() =>
    this.actions$.pipe(
      ofType("GET_DOC"),
      map(a => a), // does nothing
      tap(a => {
        console.log({ a });
      }),
      switchMap((a: any) => this.store.pipe(select(fromDocs.getById, { id: a.payload.id }))),
      tap(doc => {
        // can switch this tap to a map and see if there is a value then return this, if not then make an API call
        console.log(doc);
      }),
      exhaustMap(payload => {
        return of({ type: "SOME_ACTION", payload: null });
      })
    )
...