RxJS scan操作符首次调用时如何设置默认属性?
解决RxJS scan操作符初始值与分页状态管理问题
当前组件实现了上下加载更多的分页功能,通过scan操作符维护已加载的页码范围(minPageNumberAcquired/maxPageNumberAcquired)和数据列表,但存在两个问题:
- 服务层中手动设置
minPageNumberAcquired和maxPageNumberAcquired为当前页码,职责划分不合理,这两个状态应该由组件的状态逻辑维护 scan未设置初始值,虽然第一次API返回会直接作为初始状态,但代码逻辑依赖累加器的对比逻辑,且模板需要从初始化阶段就获取到这两个页码值来控制加载按钮的显示
步骤1:移除服务层的状态设置
修改HttpService的getProgramsSearchList方法,去掉map中对minPageNumberAcquired和maxPageNumberAcquired的赋值,让API仅返回原始分页数据:
getProgramsSearchList(searchCritieria: any): Observable<SearchPrograms> { return this.http .post<SearchPrograms>(`myapi/programs`, searchCritieria, { responseType: 'json' }) .pipe( map((clients: any) => ({ pagination: { total: clients.pagination.total, current_page: clients.pagination.current_page, ['sort-order']: clients.pagination['sort-order'], ['sort-by']: clients.pagination['sort-by'] // 移除这里的min/max设置 }, data: clients.data.map((client: any) => ({ id: client.id, name: client.meta.name })) })), catchError((err) => { return this.errorHandler(err); }) ); }
步骤2:为scan设置初始值并优化累加逻辑
给scan操作符传入符合SearchPrograms结构的初始值,同时修改累加器函数,处理第一次加载和后续上下加载的场景,并且返回新对象避免直接修改原状态对象(防止意外副作用):
search$ = this.searchCriteria$.pipe( switchMap((search: SearchProgramsCriteria) => this.httpService.getProgramsSearchList(search)), // 设置初始状态:空数据,分页字段初始化 scan((acc, curr) => { // 第一次加载:直接用当前返回值初始化状态,设置min/max为当前页码 if (acc.data.length === 0) { return { ...curr, pagination: { ...curr.pagination, minPageNumberAcquired: curr.pagination.current_page, maxPageNumberAcquired: curr.pagination.current_page } }; } // 后续加载:更新页码范围和数据列表 const newPagination = { ...acc.pagination, minPageNumberAcquired: Math.min(acc.pagination.minPageNumberAcquired, curr.pagination.current_page), maxPageNumberAcquired: Math.max(acc.pagination.maxPageNumberAcquired, curr.pagination.current_page), // 保留最新的分页排序信息 ['sort-order']: curr.pagination['sort-order'], ['sort-by']: curr.pagination['sort-by'] }; let newData = [...acc.data]; if (this.loadingDirection === "top") { newData = [...curr.data, ...newData]; } else { newData = [...newData, ...curr.data]; } return { pagination: newPagination, data: newData }; }, { // 初始值:匹配SearchPrograms接口结构 pagination: { total: 0, current_page: 0, ['sort-order']: '', ['sort-by']: '', minPageNumberAcquired: 0, maxPageNumberAcquired: 0 }, data: [] }), tap((res) => { this.unsetLoadingDirection(); }) );
关键说明
- 初始值的作用:确保模板在组件初始化时就能获取到合法的
minPageNumberAcquired和maxPageNumberAcquired值,避免出现undefined导致的模板报错 - 不可变状态:通过扩展运算符(
...)创建新对象和数组,避免直接修改累加器状态,符合RxJS的纯函数原则,防止意外的状态污染 - 职责分离:服务层仅负责数据请求和格式转换,分页状态的维护完全由组件的RxJS逻辑处理,代码结构更清晰
内容的提问来源于stack exchange,提问作者Andrew Howard
相关产品推荐
相关产品推荐

