浏览文档目录
第 15 课 · 游标列表

让无限列表保持有界和可恢复

用 initialPageParam、游标和 maxPages 建立明确的翻页契约,同时保持 pages 与 pageParams 的结构一致。

TanStack Query进阶24 分钟REV 01Markdown .md
完成后你将能够

能实现不会重复加载、不会无限占用内存并能正确判断末页的“加载更多”列表。

01

页面数据不是普通数组

无限查询缓存同时保存 data.pages 和 data.pageParams。initialPageParam 是必填起点;getNextPageParam 返回下一次 queryFn 收到的 pageParam,返回 null 或 undefined 表示没有下一页。手动修改缓存时必须保留这两个数组及其对应关系。

02

让服务端游标成为翻页事实

游标应来自上一页响应,而不是由客户端猜测。queryKey 描述列表筛选条件,pageParam 只描述这次请求的页位置;不要把不断变化的游标追加进 queryKey,否则每页会变成互不关联的查询。

src/features/issues/queries.tsts
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,
})
03

分开首屏、下一页与后台更新

按钮应同时检查 hasNextPage 与 isFetchingNextPage,防止同一次用户动作重复请求。界面要区分首次 pending、加载下一页以及后台 refetch;maxPages 可以限制内存与顺序重取成本,双向列表还必须同时定义 getPreviousPageParam。

实践

把本课变成可验证的能力

完成检查点

每次加载使用上一页返回的游标,末页后 hasNextPage 为 false,缓存中的 pages 与 pageParams 长度始终相同。

动手练习

为 Issue 时间线实现最多保留五页的“加载更多”,并在切换筛选条件后从独立缓存的第一页开始。

验证方法

记录前三次请求的游标并模拟末页、失败重试与快速双击;游标不重复、失败页可重试、同一时刻不出现重复下一页请求。

常见错误
  • 把 pageParam 放进 queryKey,让每一页成为独立 Query。
  • 手动改 pages 却漏掉 pageParams,破坏 InfiniteData 结构。
SOURCE REFERENCES · CHECKED 2026-08-03https://tanstack.com/query/latest/docs/framework/react/guides/infinite-querieshttps://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery