TanStack Query — Queries, Keys and Caching
IntermediateServer data is not application state. A query library treats it as a cache of someone else's data, which is what removes the loading flags, the races and the duplicate requests.
Overview
The insight behind TanStack Query is a distinction: the state you own — a modal being open, a draft being typed — behaves differently from data that lives on a server and can change without you. Storing server data in useState means manually maintaining a stale copy, and every feature after that is bookkeeping. A query library caches by key, deduplicates concurrent requests, refetches when data goes stale, and gives every consumer the same entry. In interviews this is the expected answer to "how do you fetch data in React", and being able to say why is the point.
A Query
A key, a function, and every state you were tracking by hand.
const { data, isLoading, isFetching, isError, error, refetch } = useQuery({
queryKey: ['problems', { topic, page }],
queryFn: ({ signal }) => listProblems({ topic, page, signal }),
staleTime: 60_000, // treat as fresh for a minute
})
if (isLoading) return <ProblemsSkeleton />
if (isError) return <ErrorState error={error} onRetry={refetch} />
return <ProblemList problems={data} />
// What you no longer write: the loading flag, the error state, the
// abort controller, the race guard, the mount check, and the
// deduplication when three components ask for the same thing.
// The key IS the cache identity. Anything the queryFn depends on
// must be in it, or you will show one topic's data under another.
queryKey: ['problems', { topic, page }] // correct
queryKey: ['problems'] // wrong — topic ignored
// Changing the key is how you refetch: no manual trigger needed.Freshness and Garbage Collection
Two timings that people confuse, and the defaults worth changing.
staleTime // how long data is considered FRESH. While fresh, no
// refetch happens at all. Default: 0 — refetch on every mount.
gcTime // how long an UNUSED cache entry is kept before deletion.
// Default: 5 minutes.
// Sensible defaults for a content site
const client = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000,
retry: 2,
refetchOnWindowFocus: false, // on by default; often surprising
},
},
})
// The lifecycle: fresh -> stale -> (refetched on mount/focus/reconnect)
// unused -> garbage collected after gcTime
// Stale data is still SHOWN. A refetch happens in the background and
// the screen updates when it lands — so the user never sees a blank
// page for data you already have. That is the whole appeal.Dependent and Parallel Queries
Ordering requests without building a waterfall by accident.
// Dependent — wait for the first result
const { data: user } = useQuery({ queryKey: ['me'], queryFn: getMe })
const { data: progress } = useQuery({
queryKey: ['progress', user?.id],
queryFn: () => getProgress(user.id),
enabled: !!user?.id, // does not run until the id exists
})
// Parallel — independent, so both start immediately
const results = useQueries({
queries: topics.map(t => ({
queryKey: ['problems', t],
queryFn: () => listProblems({ topic: t }),
})),
})
// Prefetch on intent — the page feels instant when the user arrives
<Link
to={`/problems/${slug}`}
onMouseEnter={() => queryClient.prefetchQuery({
queryKey: ['problem', slug],
queryFn: () => getProblem(slug),
})}
/>Key Points to Remember
- 1Server data is a cache of someone else's state, not application state — that distinction is the whole library
- 2The query key is the cache identity; every input the query function uses must appear in it
- 3staleTime decides when a refetch happens, gcTime decides when an unused entry is deleted
- 4Stale data stays on screen while it refetches in the background, so the user rarely sees a blank state
- 5enabled sequences dependent queries, and prefetching on hover makes navigation feel instant
Interview Questions
Sign in to ask AriaWhy treat server data differently from client state?
What happens if you leave a query parameter out of the query key?
What is the difference between staleTime and gcTime?
Ask Aria about TanStack Query — Queries, Keys and Caching
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.