intermediate

TanStack Query

Use query keys, stale time, cache time, mutations, invalidation, retries, and optimistic updates deliberately.

TanStack Query (React Query) caches async state by query keys. `useQuery` fetches and tracks status; `useMutation` writes with optional optimistic updates and invalidation.

					const { data, isLoading, error } = useQuery({
  queryKey: ['todos', filter],
  queryFn: () => fetchTodos(filter),
  staleTime: 60_000,
});

const mutation = useMutation({
  mutationFn: addTodo,
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
});
				

| Option | Role | |--------|------| | queryKey | Cache identity — include all variables | | staleTime | How long data feels fresh without refetch | | gcTime | How long unused cache survives | | enabled | Skip fetch until condition true |

Prefetch on hover, parallel queries with `useQueries`, and shared `QueryClient` for SSR dehydration.

The trade-off is powerful cache orchestration versus key design discipline and stale UI if defaults are ignored.

On interviews: key design, stale vs garbage collection, mutation + invalidation flow.

Common pitfalls: unstable query keys (inline objects), global staleTime zero causing refetch storms, and mutations without optimistic rollback plan.

Checklist:

  • Stable hierarchical query keys.
  • Tune staleTime per data type.
  • invalidateQueries or setQueryData after writes.
  • Dehydrate/hydrate for SSR carefully.