完成后你将能够
能实现不会重复加载、不会无限占用内存并能正确判断末页的“加载更多”列表。
页面数据不是普通数组
无限查询缓存同时保存 data.pages 和 data.pageParams。initialPageParam 是必填起点;getNextPageParam 返回下一次 queryFn 收到的 pageParam,返回 null 或 undefined 表示没有下一页。手动修改缓存时必须保留这两个数组及其对应关系。
让服务端游标成为翻页事实
游标应来自上一页响应,而不是由客户端猜测。queryKey 描述列表筛选条件,pageParam 只描述这次请求的页位置;不要把不断变化的游标追加进 queryKey,否则每页会变成互不关联的查询。
import { infiniteQueryOptions } from '@tanstack/react-query'
export const issuesInfiniteOptions = infiniteQueryOptions({
queryKey: ['issues', 'infinite'] as const,
initialPageParam: null as string | null,
queryFn: async ({ pageParam, signal }) => {
const search = pageParam ? `?cursor=${encodeURIComponent(pageParam)}` : ''
const response = await fetch(`/api/issues${search}`, { signal })
if (!response.ok) throw new Error('Unable to load issues')
return response.json() as Promise<{ items: Issue[]; nextCursor: string | null }>
},
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
maxPages: 5,
})分开首屏、下一页与后台更新
按钮应同时检查 hasNextPage 与 isFetchingNextPage,防止同一次用户动作重复请求。界面要区分首次 pending、加载下一页以及后台 refetch;maxPages 可以限制内存与顺序重取成本,双向列表还必须同时定义 getPreviousPageParam。
把本课变成可验证的能力
每次加载使用上一页返回的游标,末页后 hasNextPage 为 false,缓存中的 pages 与 pageParams 长度始终相同。
为 Issue 时间线实现最多保留五页的“加载更多”,并在切换筛选条件后从独立缓存的第一页开始。
记录前三次请求的游标并模拟末页、失败重试与快速双击;游标不重复、失败页可重试、同一时刻不出现重复下一页请求。
- 把 pageParam 放进 queryKey,让每一页成为独立 Query。
- 手动改 pages 却漏掉 pageParams,破坏 InfiniteData 结构。