Angular NGRX:使用 withLatestFrom 仅调用一次 api。但是,'loader' 仍然设置为 true

Angular NGRX: Use withLatestFrom to call api only once. However, 'loader' is still set to true

我创建了以下代码,试图确保在设置状态时不会再次调用 api,并避免在组件中使用订阅。

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     switchMap((action) =>
         of(action).pipe(
            withLatestFrom(this.store.select(selectPackingList)),

            filter(([action, list]) => !list || list.length === 0),

            switchMap(([action, latest]) =>
               this.packingService.getPackingList(action.request).pipe(
               map((list) => PackingApiActions.loadPackingListSuccess({ list })),
               catchError((errors) =>
                   of(PackingApiActions.loadPackingListFail({ errors }))
             )
           )
         )
       )
     )
   );
});

这或多或少是有效的(api 不再被调用)并且基于以下方面:

然而,加载属性仍然再次设置为true,这是不应该发生的:

export const packingReducer = createReducer(
  initialState,
  on(PackingPageActions.loadPackingList, (state) => ({
   ...state,
   loading: true,
})),

组件的 OnInit:

this.store.dispatch(
  PackingPageActions.loadPackingList({ request: this.PACKING_REQUEST })
);

有人知道如何解决这个问题吗?

谢谢!

首先你可以稍微清理一下流:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     filter(([action, list]) => !list || list.length === 0),
     switchMap(([action, latest]) =>
       this.packingService.getPackingList(action.request).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     )
   );
});

不需要外部 switchMapof,只是多余的。

其次,您还需要对 reducer 进行类似的检查,以确保它不会被设置,除非 effect 真正生效:

export const packingReducer = createReducer(
  initialState,
  on(PackingPageActions.loadPackingList, (state) => ({
   ...state,
   loading: !state.whatever.the.path.to.the.list.is?.length,
})),

或者不过滤,您可以使用已经获取的列表模拟响应:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     switchMap(([action, latest]) =>
       ((latest && latest.length) ? of(latest) : this.packingService.getPackingList(action.request)).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     )
   );
});

你可以用其他方式做到这一点,比如在那里有另一个动作来表明请求实际上正在发出,比如 loadPackingListRequest 请求效果将在请求发出信号之前发出,以表明它实际上正在发生,并且这就是将 loading 状态设置为 true 的原因,这种方式可能更清晰,因为您对 reducer 和 effects 之间的同步代码没有隐藏的依赖性。但在这一点上它是首选。

编辑:

第三个行动策略看起来有点像这样:

loadPackingList$ = createEffect(() => {
   return this.actions$.pipe(
     ofType(PackingPageActions.loadPackingList),
     withLatestFrom(this.store.select(selectPackingList)),
     filter(([action, list]) => !list || list.length === 0),
     switchMap(([action, latest]) => from([
       of(PackingApiActions.loadingPackingList()),
       this.packingService.getPackingList(action.request).pipe(
         map((list) => PackingApiActions.loadPackingListSuccess({ list })),
         catchError((errors) =>
           of(PackingApiActions.loadPackingListFail({ errors }))
         )
       )
     ]).pipe(mergeAll()))
   );
});

这样你的效果会发出两个动作,一个表示它实际上正在加载,另一个表示它是失败还是成功。

然后修改 reducer 以查看加载操作,而不是将加载设置为 true:

export const packingReducer = createReducer(
  initialState,
  on(PackingApiActions.loadingPackingList, (state) => ({
   ...state,
   loading: true,
})),