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

使用useInfiniteQuery与InfiniteScroll时如何避免首段数据重复加载?

解决useInfiniteQuery + react-infinite-scroll-component首次重复加载数据问题

问题根源

你遇到的重复加载、无效页码问题,核心原因通常是这几点:

  1. 分页参数控制不明确:依赖后端默认返回第一页,前端未显式传递page参数,导致首次fetchNextPage时仍用初始无参数的请求逻辑,重复拉取第一页;
  2. getNextPageParam逻辑错误:未正确判断当前页码与总页数的关系,导致返回了超出范围的页码;
  3. hasMore属性未正确绑定:react-infinite-scroll-component的hasMore未关联useInfiniteQuery的hasNextPage,导致无意义的加载触发。

具体修复方案

1. 显式控制分页参数,统一请求逻辑

不要依赖后端默认的page值,前端主动管理分页参数,初始请求就传递page=1,确保所有分页请求的参数逻辑一致。

2. 正确实现getNextPageParam

从接口返回的响应中提取当前页码和总页数(或通过返回数据长度判断),只有当当前页码小于总页数时,才返回下一页的页码,否则返回undefined(终止加载)。

3. 绑定hasMore与hasNextPage

将react-infinite-scroll-component的hasMore直接绑定到useInfiniteQuery返回的hasNextPage,避免无效的加载触发。


完整示例代码

import { useInfiniteQuery } from '@tanstack/react-query';
import InfiniteScroll from 'react-infinite-scroll-component';

// 自定义请求函数,显式接收pageParam
const fetchListData = async ({ pageParam = 1, sort, limit }) => {
  const res = await fetch(`/api/your-list?page=${pageParam}&sort=${sort}&limit=${limit}`);
  const data = await res.json();
  
  // 确保接口返回当前页码和总页数(或根据返回数据长度判断)
  return {
    items: data.items,
    currentPage: pageParam,
    totalPages: data.totalPages,
    // 如果没有总页数,可用以下逻辑替代:
    // hasMore: data.items.length === limit
  };
};

const InfiniteList = ({ sort = 'desc', limit = 10 }) => {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage
  } = useInfiniteQuery({
    // 查询键包含所有非分页参数,确保参数变化时重新发起请求
    queryKey: ['infinite-list', sort, limit],
    queryFn: ({ pageParam }) => fetchListData({ pageParam, sort, limit }),
    // 核心:正确计算下一页参数
    getNextPageParam: (lastPage) => {
      // 方式1:基于总页数判断
      if (lastPage.currentPage < lastPage.totalPages) {
        return lastPage.currentPage + 1;
      }
      // 方式2:基于返回数据长度判断(无总页数时用)
      // if (lastPage.items.length === limit) {
      //   return lastPage.currentPage + 1;
      // }
      return undefined; // 无更多数据时返回undefined
    },
    initialPageParam: 1 // 显式设置初始页码
  });

  // 计算已加载数据的总长度,用于InfiniteScroll判断滚动位置
  const totalLoadedItems = data?.pages.reduce((sum, page) => sum + page.items.length, 0) || 0;

  return (
    <InfiniteScroll
      dataLength={totalLoadedItems}
      next={fetchNextPage}
      hasMore={hasNextPage} // 直接绑定useInfiniteQuery的hasNextPage
      loader={<div style={{ padding: '20px', textAlign: 'center' }}>加载中...</div>}
      endMessage={<div style={{ padding: '20px', textAlign: 'center' }}>已加载全部数据</div>}
    >
      {data?.pages.map((page, pageIndex) => (
        <div key={`page-${pageIndex}`}>
          {page.items.map(item => (
            <div key={item.id} style={{ padding: '10px', borderBottom: '1px solid #eee' }}>
              {item.title}
            </div>
          ))}
        </div>
      ))}
      {isFetchingNextPage && <div style={{ padding: '20px', textAlign: 'center' }}>加载更多...</div>}
    </InfiniteScroll>
  );
};

export default InfiniteList;

额外注意事项

  • 确保接口返回的数据中包含当前页码和总页数(或能通过返回数据长度判断是否还有更多数据);
  • 查询键queryKey必须包含所有影响请求结果的参数(如sort、limit),避免参数变化时缓存冲突;
  • 检查组件中是否有其他逻辑意外调用fetchNextPage,比如不必要的useEffect触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 08:40:17