使用useInfiniteQuery与InfiniteScroll时如何避免首段数据重复加载?
解决useInfiniteQuery + react-infinite-scroll-component首次重复加载数据问题
问题根源
你遇到的重复加载、无效页码问题,核心原因通常是这几点:
- 分页参数控制不明确:依赖后端默认返回第一页,前端未显式传递
page参数,导致首次fetchNextPage时仍用初始无参数的请求逻辑,重复拉取第一页; getNextPageParam逻辑错误:未正确判断当前页码与总页数的关系,导致返回了超出范围的页码;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
相关产品推荐
相关产品推荐

