如何在 ngrx 中将 withLatestFrom 与选择器一起使用?

Jon*_*Sud 4 rxjs ngrx angular

我有 ngrx 应用程序,我无法通过我在组件中发送的有效负载从效果中的选择器中获取值。

我写了一个我处理的示例代码:

这是我的状态,里面有一个简单的 docs 数组。

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

export const reducer = createReducer(
  initialState,
);
Run Code Online (Sandbox Code Playgroud)

我不处理这个问题的任何行动,这不是问题。

在我的组件中,我使用选择器获取数组:

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

{{docs$ | async | json}}
Run Code Online (Sandbox Code Playgroud)

减速机:

export const selectDocsState = createFeatureSelector("docs");

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

export const getAllDocs = createSelector(
  selectDocsStatusState,
  getDocs
)
Run Code Online (Sandbox Code Playgroud)

我还有一个通过 id 获取的选择器:

{{docsById$ | async | json }}

docsById$ = this.store.pipe(select(fromStore.getById, { id: 1 }));
Run Code Online (Sandbox Code Playgroud)

在减速器中:

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

到目前为止一切都很好。我得到 docs 数组并显示它。

现在,在我的组件中,我分派了一些动作并按效果捕捉:

this.store.dispatch({ type: 'GET_DOC', payload: { id: 2 } });
Run Code Online (Sandbox Code Playgroud)

结果我想知道这个 id 是否在商店里。

所以我使用withLatestFromfrom rxjs 和 getById 选择器。

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

问题是我没有从选择器那里得到值,我得到了商店实例。

tap(v => {
  console.log({ v });
}),
Run Code Online (Sandbox Code Playgroud)

完整效果:

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 });
      })
    )
  );
Run Code Online (Sandbox Code Playgroud)

这是我在 stackbliz 中的完整示例。

我在这里想念什么?

And*_*len 13

这在NgRx Docs - Incorporating State 中有介绍,但我发现Brandon Roberts github 评论更有用:


使用 ofType 运算符后,在监听动作时,您主要做两件事:

actions.pipe(
  ofType('SOME_ACTION')
  someMap(action => doSomething(action))
)
Run Code Online (Sandbox Code Playgroud)

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

就效果而言,它们有两种不同的行为。第一个在动作被调度之前不做任何事情,这是它应该做的。

第二个立即订阅 withLatestFrom ,无论该操作是否已调度。如果选择器使用的那个状态还没有初始化,你可能会得到一个你意想不到的错误。为了让它“懒惰”,你最终不得不将它嵌套在一个扁平化的可观察操作符中。

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

应用它给出以下内容:

  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();
          })
        )
      )
    );
  });
Run Code Online (Sandbox Code Playgroud)

闪电战

  • 这真是金黄啊,谢谢分享。我有一个效果需要访问尚未初始化的商店,这挽救了这一天。 (3认同)

Vin*_*-cm 9

使用 Ngrx 中的 concatLatestFrom(withLatestFrom 的惰性版本)来缓解原始状态在需要之前尚未启动的情况。

import {Actions, concatLatestFrom, createEffect, ofType} from '@ngrx/effects';

concatLatestFrom(() => this.store.select(fromDocs.getById, { id: action.payload.id }),
      map(([action, latest]) => {
        return fromDocs.someAction();
      })
Run Code Online (Sandbox Code Playgroud)