Capítulo 24 de 80

Chapter 24: Initial Query Data

Core Idea

initialData seeds the cache with real data and skips the initial loading state entirely — unlike placeholderData, it persists to the cache, so it must be treated as trustworthy data, not a partial/fake preview (that's what placeholder data is for).

Key Concepts

  • Three ways to seed a cache before it's needed: declaratively via initialData on the query itself; imperatively via prefetching (queryClient.query) or a direct queryClient.setQueryData write.
  • Freshness interaction with staleTime: initialData is treated as freshly-fetched by default, so with the default staleTime: 0 the query shows the initial data immediately and immediately refetches on mount; with a positive staleTime, the data is considered fresh for that window before any refetch.
  • initialDataUpdatedAt: the accurate way to represent initial data that isn't actually fresh — pass the real timestamp (ms) it was captured at, and let staleTime do its normal freshness math against that real age instead of pretending the data was just fetched.
  • Function form: pass a function instead of a value when computing initial data is expensive — it only runs once, at query initialization, not on every render.
  • Deriving from another query's cache: a common pattern is looking up one item from a list query's cached data (queryClient.getQueryData(['todos'])?.find(...)) as initialData for an individual-item query — pair this with initialDataUpdatedAt: () => queryClient.getQueryState(['todos'])?.dataUpdatedAt so staleness is computed against the source query's real age, not assumed to be "just fetched."
  • Conditional use of cached data: when the source query might be too old to trust as initial data, check queryClient.getQueryState(['todos'])?.dataUpdatedAt against a freshness threshold before deciding to use it at all — return undefined to fall back to a normal hard loading fetch instead.

Code Examples

// Naive: initialData treated as fresh right now (refetches immediately with staleTime: 0)
useQuery({ queryKey: ['todos'], queryFn: () => fetch('/todos'), initialData: initialTodos })

// Accurate: initialData's real age is known, staleTime governs it correctly
useQuery({
  queryKey: ['todos', todoId],
  queryFn: () => fetch(`/todos/${todoId}`),
  initialData: () => queryClient.getQueryData(['todos'])?.find((d) => d.id === todoId),
  initialDataUpdatedAt: () => queryClient.getQueryState(['todos'])?.dataUpdatedAt,
})
  • What it demonstrates: the difference between treating derived initial data as artificially fresh vs. giving it its real timestamp so staleTime decides refetching correctly.

Key Takeaways

  1. initialData is persisted to the cache — never pass partial/fake/placeholder-shaped data here, that's exactly what placeholderData (Chapter 25) is for.
  2. When deriving initial data from another query's cache, always pair it with initialDataUpdatedAt sourced from that query's real dataUpdatedAt — otherwise you're lying to staleTime about how fresh the data is.
  3. Use the function form of initialData whenever computing it isn't free — it runs exactly once per query initialization.

Connects To

  • Placeholder Query Data: the non-persisted alternative for genuinely partial/fake data.
  • Important Defaults: how staleTime governs refetch timing, which initialDataUpdatedAt feeds into accurately.