Capítulo 22 de 80

Chapter 22: Paginated / Lagged Queries

Core Idea

Naively keying a query by page number treats each page as a brand-new query, so the UI flashes between success and pending on every page change — placeholderData: keepPreviousData keeps the previous page's data on screen while the next page loads instead.

Key Concepts

  • The naive problem: useQuery({ queryKey: ['projects', page], queryFn: () => fetchProjects(page) }) "works," but each page value is a distinct cache entry, so switching pages destroys/creates queries and the UI drops to a loading state every time.
  • keepPreviousData: passed as placeholderData, it keeps the last successful page's data visible while the new page's key is fetching, then swaps seamlessly once the new data arrives — no hard loading flash.
  • isPlaceholderData: exposed on the result to distinguish "this is stale data from the previous page, still fetching the real one" from genuinely fresh data — use it to disable a "Next" button until you know a next page actually exists, since the currently-shown data might be the previous page's.
  • Works with infinite queries too: placeholderData (with keepPreviousData) applies equally to useInfiniteQuery, letting cached pages stay visible while the infinite query's key changes.

Code Examples

import { keepPreviousData, useQuery } from '@tanstack/react-query'

const { data, isFetching, isPlaceholderData } = useQuery({
  queryKey: ['projects', page],
  queryFn: () => fetchProjects(page),
  placeholderData: keepPreviousData,
})

// disabled until we know the *current* (non-placeholder) data confirms a next page
<button disabled={isPlaceholderData || !data?.hasMore} onClick={() => setPage((p) => p + 1)}>
  Next Page
</button>
  • What it demonstrates: keepPreviousData eliminating the flash between pages, and isPlaceholderData gating a "Next" action on genuinely-fresh data.

Key Takeaways

  1. Any UI where the query key changes on user interaction (pagination, tabs, filters) benefits from placeholderData: keepPreviousData — it's the general fix, not something specific to numeric pages.
  2. Gate irreversible or data-dependent actions (like "is there a next page?") on !isPlaceholderData, since the visible data might still be the previous key's result.
  3. This is the same mechanism used for cursor-based/infinite lists — it isn't limited to classic page-number pagination.

Connects To

  • Infinite Queries: placeholderData applies there too.
  • Query Keys: the reason page-keyed queries are treated as distinct entries in the first place.