Angular13+NgRx分步加载列表及状态的正确实现方案咨询
问题分析与解决方案:Angular 13 + NgRx分步加载列表及状态
场景需求
- 第一步:通过单次API请求快速获取基础条目列表
- 第二步:待基础列表加载完成后,逐个发起API请求加载每个条目的状态(该请求耗时较长)
当前代码的核心问题
- 在
map操作符内手动订阅内部API请求,不符合NgRx Effect的设计规范:Effect必须返回Observable动作流,而非手动订阅触发动作 - 内部订阅产生的
GET_QUEUE_UPDATESTATUS动作无法被NgRx的Action流捕获,导致状态更新失效 - 未处理内部状态请求的错误,可能引发未捕获异常
正确实现方案
修正后的Effect代码
getDatafacts$ = createEffect(() => this.actions$.pipe( ofType(DataFactActions.GET_QUEUE_START), switchMap(() => this.apiService.callApi("GET", "neotimo/datafact/getdatafactqueue", null).pipe( // 先分发基础列表加载成功的动作,确保用户快速看到基础数据 tap(result => { const queue = <DataFactQueueResult>result; this.store.dispatch(DataFactActions.GET_QUEUE_SUCCESS({ payload: queue })); }), // 处理每个条目的状态请求 switchMap(result => { const queue = <DataFactQueueResult>result; // 生成每个条目对应的状态请求Observable const statusRequests$ = queue.dataFactItems.map(item => this.apiService.callApi("GET", "neotimo/datafact/getdatafactitem", item.id.toString()).pipe( map(status => DataFactActions.GET_QUEUE_UPDATESTATUS({ payload: <DataFactItemResult>status })), catchError(error => { // 单个状态请求失败的处理,可根据需求分发错误动作或忽略 console.error(`加载条目${item.id}状态失败`, error); return of(); // 返回空Observable,不中断其他请求 }) ) ); // 并行执行所有状态请求,若需串行则替换为concat(...statusRequests$) return forkJoin(statusRequests$); }), catchError(errorRes => { switch (errorRes.status) { case 403: case 401: return of(DataFactActions.GET_QUEUE_FAIL({ payload: [this.errorAccessDenied] })); case 404: return of(DataFactActions.GET_QUEUE_FAIL({ payload: [this.error404] })); case 400: return of(DataFactActions.GET_QUEUE_FAIL({ payload: [errorRes.error.errors[0].message] })); default: return of(DataFactActions.GET_QUEUE_FAIL({ payload: [this.errorOccured] })); } }) ) ) ));
Reducer处理逻辑
Reducer需要处理三类核心动作:
GET_QUEUE_SUCCESS:初始化状态中的基础条目列表,让用户快速看到内容GET_QUEUE_UPDATESTATUS:根据返回的条目状态,更新对应条目的状态字段GET_QUEUE_FAIL:处理基础列表加载失败的错误状态
示例Reducer结构:
const initialState = { queueItems: [], error: null, loading: false }; export function dataFactReducer(state = initialState, action: DataFactActions) { switch (action.type) { case DataFactActions.GET_QUEUE_START: return { ...state, loading: true, error: null }; case DataFactActions.GET_QUEUE_SUCCESS: return { ...state, loading: false, queueItems: action.payload.dataFactItems }; case DataFactActions.GET_QUEUE_UPDATESTATUS: return { ...state, queueItems: state.queueItems.map(item => item.id === action.payload.id ? { ...item, ...action.payload } : item ) }; case DataFactActions.GET_QUEUE_FAIL: return { ...state, loading: false, error: action.payload }; default: return state; } }
关键设计点说明
- 用户体验优先:先通过
tap分发GET_QUEUE_SUCCESS,确保基础列表快速展示,再异步加载状态 - 请求模式选择:用
forkJoin并行执行状态请求提升效率,若后端有并发限制,可替换为concat实现串行请求 - 错误隔离:单个状态请求失败不中断其他请求,基础列表请求失败统一处理错误状态
内容的提问来源于stack exchange,提问作者Emmanuel Vaussion
相关产品推荐
相关产品推荐

