我创建了以下代码,试图确保设置状态时不会再次调用 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 不会再次调用),并且基于以下内容: How to send request to the server only once by using @NgRx/effects
但是,loading 属性仍然再次设置为 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 }))
)
)
)
);
});
不需要将外部switchMap
放入of
中,只是多余。
其次,您还需要对您的 reducer 进行类似的检查,以确保它不会被设置,除非效果真正生效:
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 和效果之间没有同步代码的隐藏依赖。但这是目前的偏好。
编辑:
第三个行动策略看起来有点像这样:
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以查看加载操作,而不是将loading设置为true:
export const packingReducer = createReducer(
initialState,
on(PackingApiActions.loadingPackingList, (state) => ({
...state,
loading: true,
})),
关于Angular NGRX : Use withLatestFrom to call api only once. 但是, 'loader' 仍然设置为 true,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69886854/