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 }))
)
)
)
);
});
不需要外部 switchMap
到 of
,只是多余的。
其次,您还需要对 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,
})),
我创建了以下代码,试图确保在设置状态时不会再次调用 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 }))
)
)
)
);
});
不需要外部 switchMap
到 of
,只是多余的。
其次,您还需要对 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,
})),