You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Query如何获取保留历史数据 解决滚动加载旧数据丢失问题

问题根因

当前代码配置的keepPreviousData: true仅会在新页码请求加载阶段临时保留上一页结果,避免页面出现加载空白。新页码请求成功后,当前query key [ACTIVITY_FEED, activityFeedPageIndex] 对应的返回值会直接替换为最新页数据。虽然不同页码的历史查询结果实际以独立缓存条目的形式存在于React Query缓存中(每个页码对应单独缓存),但未做跨页数据聚合时,渲染层只能拿到当前页内容,表现为旧数据丢失。

可行实现方案

方案1:使用官方useInfiniteQuery(无限滚动场景首选)

React Query原生提供了适配无限滚动/分页加载场景的useInfiniteQuery Hook,内置历史数据聚合、页码维护、加载状态标识能力,无需手动编写数据拼接逻辑。
实现示例:

const useActivityFeedData = (pageSize = Config.defaultPageSize) =>
  useInfiniteQuery(
    [ACTIVITY_FEED],
    ({ pageParam = 1 }) => Api.user.getActivityFeedData(pageParam, pageSize),
    {
      initialPageParam: 1,
      // 定义下一页页码的计算规则
      getNextPageParam: (lastPage, allPages) => {
        // 按实际接口逻辑调整:当前页数据量小于页大小则判定为没有下一页
        return lastPage.activityList.length < pageSize ? undefined : allPages.length + 1;
      },
      select: (res) => ({
        // 拍平所有已加载页数据,得到包含全部历史内容的列表
        activityList: res.pages.flatMap(page => page.activityList),
      }),
    }
  );

// 组件内调用
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useActivityFeedData();
// 滚动触底回调
const handleReachBottom = () => {
  if (hasNextPage && !isFetchingNextPage) fetchNextPage();
};
// 直接渲染data.activityList即可拿到全量历史加载数据

该方案优势:

  • 自动按页码缓存数据,回退到已加载页码时不会重复发请求
  • 内置下一页判断、加载状态标识,无需额外维护页码状态
  • 原生支持双向加载(可同时实现下拉加载更早历史、上拉拉取最新内容)

方案2:保留现有useQuery写法,手动聚合缓存数据

如果受业务逻辑限制无法替换为useInfiniteQuery,可以通过React Query提供的getQueryData方法,手动读取缓存中所有已加载页码的数据做聚合。
修改后的Hook代码:

const useActivityFeedData = (
  activityFeedPageIndex: number,
  pageSize = Config.defaultPageSize,
) => {
  const queryClient = useQueryClient();
  return useQuery(
    [ACTIVITY_FEED, activityFeedPageIndex],
    () => Api.user.getActivityFeedData(activityFeedPageIndex, pageSize),
    {
      keepPreviousData: true,
      select: (res: any) => {
        const allActivities = [];
        // 读取所有已加载页码的缓存数据
        for (let page = 1; page <= activityFeedPageIndex; page++) {
          const cachedPage = queryClient.getQueryData([ACTIVITY_FEED, page]);
          if (cachedPage?.activityList) {
            allActivities.push(...cachedPage.activityList);
          }
        }
        // 补充当前页最新返回数据,避免缓存更新延迟导致的数据缺失
        allActivities.push(...res.activityList);
        // 按业务唯一id去重,避免重复渲染
        const uniqueActivityList = Array.from(
          new Map(allActivities.map(item => [item.id, item])).values()
        );
        return { activityList: uniqueActivityList };
      },
    },
  );
}

该方案需要自行维护页码累加、数据去重、是否存在下一页的判断逻辑,维护成本更高,仅推荐在特殊定制场景下使用。

避坑提示
  • 不要将所有页数据手动拼接后存入固定query key的缓存中,会破坏React Query原生的缓存失效、自动重试、垃圾回收机制,容易引发数据不一致、内存泄漏问题
  • 两种方案都支持通过setQueryData精准更新单条缓存数据,无需在单条内容变更时全量重拉所有页数据

内容的提问来源于stack exchange,提问作者Fathe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:12:14