React: From Components to Production · Fetching and Caching Server Data

Server State with TanStack Query

Cache, dedupe, refetch and paginate server data with useQuery.

TanStack Query (v5) manages server state: caching by query key, deduplicating requests, background refetching, retries, pagination and invalidation. useQuery({ queryKey, queryFn }) returns data, isPending, isError and more. Data is "fresh" for staleTime, then refetched on mount, focus or reconnect. Keys include every variable the query depends on.

import { QueryClient, QueryClientProvider, useQuery, keepPreviousData } from "@tanstack/react-query";

const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60_000 } } });

function useOrders(page: number, status: string) {
  return useQuery({
    queryKey: ["orders", { page, status }],
    queryFn: async ({ signal }) => {
      const res = await fetch(`/api/orders?page=${page}&status=${status}`, { signal });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      return (await res.json()) as { items: Order[]; total: number };
    },
    placeholderData: keepPreviousData,   // keep showing the old page while loading the next
  });
}

function Orders() {
  const [page, setPage] = useState(1);
  const { data, isPending, isError, error, isFetching } = useOrders(page, "paid");
  if (isPending) return <Spinner />;
  if (isError) return <ErrorMessage error={error} />;
  return <OrderTable items={data.items} loading={isFetching} onPage={setPage} />;
}

// <QueryClientProvider client={queryClient}><App /></QueryClientProvider>

Common pitfalls

  • Query keys missing variables, so different requests share a cache entry.
  • Copying query data into local state (it goes stale).
  • Default staleTime 0 causing many refetches — set a sensible value.

Key points

  • Query keys identify cached data
  • staleTime controls refetching
  • Don't copy server state into useState

Further reading: TanStack Query docs

Practice questions

Why must all variables used by queryFn be in the queryKey?

Answer

The key identifies the cached data. If a variable (like page) isn't in the key, different pages share one cache entry and the UI shows wrong data.

What's the difference between isPending and isFetching?

Answer

isPending means there's no data yet (first load). isFetching is true whenever a request is in flight, including background refetches when cached data is already shown.

Open in the app to mark lessons complete, take notes and track your progress.