浏览文档目录
Agent docs/query/infinite-cursor
recipe

建立有界的游标 Infinite Query

用一份 queryKey 管理整个列表,让服务端游标驱动 pageParam,并用 maxPages 控制缓存与重取成本。

View raw Markdown
CONTRACT

执行契约

适用场景
一个列表需要追加加载页面,并由服务端游标或明确 offset 决定下一页。
不要使用
普通分页需要可分享页码和随机跳页时,优先把 page 放入 URL 并使用常规 Query。
前置检查
定义 initialPageParam、末页信号、稳定 Query Key、页面响应形状以及是否需要 maxPages 或双向翻页。
验证结果
记录连续 pageParam,验证末页、失败重试、筛选切换与重复点击;pages 和 pageParams 必须始终一一对应。
失败模式
重复页通常来自客户端猜测游标、getNextPageParam 返回旧值或并发触发 fetchNextPage;从网络记录和最后一页响应定位。
安全边界
游标是外部输入且可能被篡改;服务端必须验证作用域、排序与用户权限,不能把游标当作授权令牌。
01

输入契约

确定第一页 pageParam、响应中的 nextCursor、末页信号以及筛选条件。筛选属于 queryKey,游标属于 pageParam。后端若使用 offset,也要从 lastPageParam 明确推导,不能依赖组件中的局部页码。

02

最小实现

initialPageParam 与 getNextPageParam 在 v5 中为必需契约。返回 undefined 结束列表;用 AbortSignal 停止过期列表请求。maxPages 大于 0 时,双向翻页还必须提供 getPreviousPageParam。

src/features/issues/queries.tsts
import { infiniteQueryOptions } from '@tanstack/react-query'

export const issueFeedOptions = infiniteQueryOptions({
  queryKey: ['issues', 'feed'] as const,
  initialPageParam: null as string | null,
  queryFn: ({ pageParam, signal }) => fetchIssuePage(pageParam, signal),
  getNextPageParam: (page) => page.nextCursor ?? undefined,
  maxPages: 5,
})
03

缓存与交互检查

渲染前 flatten data.pages,但写缓存时保留 `{ pages, pageParams }`。加载按钮由 hasNextPage 与 isFetchingNextPage 共同控制;测试末页、失败重试、筛选切换、快速重复触发以及 maxPages 淘汰方向。

PRIMARY SOURCEShttps://tanstack.com/query/latest/docs/framework/react/guides/infinite-querieshttps://tanstack.com/query/latest/docs/framework/react/reference/useInfiniteQuery