Capítulo 23 de 80
useInfiniteQuery manages a single cache entry holding an ordered array of fetched pages (data.pages + data.pageParams), advanced by fetchNextPage/fetchPreviousPage, with getNextPageParam/getPreviousPageParam deciding whether more data exists and what parameter to request it with.
useQuery: data is { pages, pageParams } instead of a flat value; fetchNextPage/fetchPreviousPage trigger loading more; initialPageParam is required to seed the first request; hasNextPage/hasPreviousPage derive from whether getNextPageParam/getPreviousPageParam return a defined value; isFetchingNextPage/isFetchingPreviousPage distinguish "loading more" from a background refresh.fetchNextPage while a fetch is already in flight risks overwriting a concurrent background refresh — guard calls with !isFetching (e.g. onEndReached={() => hasNextPage && !isFetching && fetchNextPage()}), or opt into concurrent fetches explicitly via fetchNextPage({ cancelRefetch: false }) (default cancelRefetch is true).getPreviousPageParam/fetchPreviousPage/hasPreviousPage/isFetchingPreviousPage alongside the "next" set for lists that load in both directions.select to return { pages: [...data.pages].reverse(), pageParams: [...data.pageParams].reverse() } rather than reversing in the render function.queryClient.setQueryData write must preserve the { pages, pageParams } shape — e.g. pages: data.pages.slice(1) to drop the first page, always updating both arrays together.maxPages: caps how many pages stay in the cache (used with both getNextPageParam and getPreviousPageParam) — bounds memory usage and how many pages get refetched sequentially when the query goes stale.getNextPageParam/getPreviousPageParam also receive the current page's own pageParam as a third argument, so you can compute the next one (lastPageParam + 1) even when the API response carries no cursor field itself.const { data, fetchNextPage, hasNextPage, isFetchingNextPage, status } = useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam }) => fetch('/api/projects?cursor=' + pageParam).then((r) => r.json()),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
// Safe "load more" trigger — guards against overlapping in-flight fetches
<List onEndReached={() => hasNextPage && !isFetching && fetchNextPage()} />
fetchNextPage.fetchNextPage() calls with !isFetching (or accept the cancelRefetch: false risk deliberately) — a single cache entry means overlapping fetches can overwrite each other.maxPages proactively for lists that can grow to dozens of pages — it bounds both memory and the sequential-refetch cost when the query goes stale.pages and pageParams together and keep them the same length/order — partial edits break the pagination model.keepPreviousData applies to infinite queries as well.pageParam arrives via the same QueryFunctionContext covered there.