【问题标题】:How to use withLatestFrom with a selector in ngrx?如何在ngrx中将withLatestFrom与选择器一起使用?
【发布时间】:2020-06-02 13:55:12
【问题描述】:

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

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

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

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);
  }
)

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

现在,在我的组件中,我调度了一些动作并按效果捕获:

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

实际上我想知道这个 id 是否在商店中。

所以我使用来自 rxjs 的 withLatestFrom 和 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 });
      })
    )
  );

Here is my full example in stackbliz.

我在这里错过了什么?

【问题讨论】:

标签: angular rxjs ngrx


【解决方案1】:

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 });
      })
    )

【讨论】:

  • No.. 我还想要动作对象,所以我使用 withLatestFrom 来获取动作和来自商店的最新消息...
  • SwitchMap 采用复合函数,因此您可以将操作的句柄作为参数 (a: any) 并切换到存储中的最新值。
【解决方案2】:

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


在使用 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,无论该操作是否已调度。如果选择器使用的那个状态还没有初始化,你可能会得到一个你没有预料到的错误。为了让它“懒惰”,你最终不得不将它嵌套在一个扁平的 observable 运算符中。

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

【讨论】:

  • 这是因为从使用链式方法重构为使用pipe。在对pipe 的调用中立即评估参数列表,而不是控制下一次调用何时发生的链中的每个函数调用。如果是这样,我会说这是一次重构,实际上使设计变得更糟而不是更好。
  • 这真是太棒了,谢谢分享。我有一个需要访问尚未初始化的商店的效果,这节省了时间。
  • 查看(并点赞)回复concatLatestFrom 看起来比我的好
【解决方案3】:

使用 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();
      })

【讨论】:

  • 这是一个更好的解决方案并减少了错误。我们在整个应用程序中都使用它。
猜你喜欢
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-17
  • 2017-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多