React Query实现无限滚动时如何拼接旧数据与新加载数据
问题原因
你当前的实现无法累加数据的核心原因是:每次页码变化时,useQuery的queryKey[ACTIVITY_FEED, activityFeedPageIndex]会同步变化,React Query会将不同页码识别为独立的查询请求,各自维护独立缓存。而select方法只能访问当前单次请求返回的响应数据,无法跨查询拿到之前页码的旧缓存,自然没法直接完成数据拼接。
推荐实现(官方标准方案)
无限滚动场景优先使用React Query官方提供的useInfiniteQueryHook,它原生支持多页数据缓存、累加逻辑,不需要手动处理旧数据获取:
const useActivityFeedData = (pageSize = Config.defaultPageSize) => useInfiniteQuery( [ACTIVITY_FEED], ({ pageParam = 1 }) => Api.user.getActivityFeedData(pageParam, pageSize), { initialData: { pages: [{ activityList: [] }], pageParams: [1], }, // 计算下一页请求的页码 getNextPageParam: (lastPage, allPages) => { // 按实际接口逻辑调整:当前页返回数据不足pageSize时说明没有更多数据 if (lastPage.activityList.length < pageSize) return undefined; return allPages.length + 1; }, select: (data) => ({ // 拍平所有分页数据得到累加后的完整列表 activityList: data.pages.flatMap(page => page.activityList), pageParams: data.pageParams }), } );
调用方式
- 不需要在外部维护页码状态,Hook内部会自动管理分页参数
- 从返回结果中取
activityList即可拿到累加后的全量列表 - 滚动到底部触发加载时,直接调用返回的
fetchNextPage方法拉取下一页 - 可通过返回的
hasNextPage判断是否还有剩余数据,isFetchingNextPage判断下一页加载状态,避免重复发起请求
兼容原有useQuery写法的实现
如果你不想替换为useInfiniteQuery,可以通过queryClient手动获取上一页缓存完成拼接,注意这种方式仅支持连续页码的线性加载,跳页、缓存回收都会导致数据断层:
import { useQueryClient } from '@tanstack/react-query'; const useActivityFeedData = ( activityFeedPageIndex: number, pageSize = Config.defaultPageSize, ) => { const queryClient = useQueryClient(); return useQuery( [ACTIVITY_FEED, activityFeedPageIndex], () => Api.user.getActivityFeedData(activityFeedPageIndex, pageSize), { initialData: { activityList: [], }, keepPreviousData: true, select: (res: any) => { // 第一页直接返回当前数据 if (activityFeedPageIndex === 1) { return { activityList: res.activityList }; } // 读取上一页的缓存数据 const prevPageCache = queryClient.getQueryData<{activityList: any[]}>([ ACTIVITY_FEED, activityFeedPageIndex - 1 ]); // 拼接新旧数据返回 return { activityList: [...(prevPageCache?.activityList || []), ...res.activityList] }; }, }, ); };
内容的提问来源于stack exchange,提问作者Fathe
相关产品推荐
相关产品推荐

