Sikau

React patterns at scale · Day 7 · about 40 min

Caching server data

Deduplicate requests and stale-while-revalidate.

Three spinners for one dish

The menu page shows the dish name in the header, the card and the cart. Each component fetches it in its own useEffect: three requests, three spinners, and when you come back to the page, three more. The data is identical every time. Multiply that by every screen in a large app and the server, the network and the user all pay.

The field guide's data-fetching chapter draws the key distinction: server state isn't like a checkbox's state. It lives on a server, other people can change it, and your copy can go stale. That's why libraries like React Query (TanStack Query), SWR and Apollo exist — each is a cache with rules. Today you'll build the core of one, so those libraries stop being magic.

A cache with three rules

  • Deduplicate: if a request for this key is already in flight, return its promise instead of starting another — that's request deduplication.
  • Serve fresh data from the cache: data younger than staleTime is returned without a request. React Query's default staleTime is 0 (always refetch in the background); the book shows setting it to a minute.
  • Stale-while-revalidate: when data is stale, keep showing it while a background refetch runs, then swap in the new data. The name comes from an HTTP caching directive.
  • Invalidate after a change: invalidate(key) marks data stale so the next read refetches — cache invalidation.
function fetchQuery(key, fetcher) {
  const entry = cache.get(key);
  if (entry?.promise) return entry.promise;
  if (entry?.status === "success" && Date.now() - entry.updatedAt < STALE_TIME)
    return Promise.resolve(entry.data);
  const promise = fetcher().then(
    (data) => { setEntry(key, { status: "success", data, updatedAt: Date.now(), promise: null }); return data; },
    (error) => { setEntry(key, { status: "error", error, promise: null }); throw error; },
  );
  setEntry(key, { status: entry?.status ?? "loading", promise });
  return promise;
}

The rest of this lesson

  • Connecting a store to Reactexplain
  • Two at oncepredict
  • Stale-while-revalidateorder
  • Cache vocabularymatch
  • Build a query cachewrite code
  • Badge quizquiz
  • What you learnedrecap

Continue this lesson free — run real code in your browser

Continue with Google

See all 10 lessons of React patterns at scale