Stop 09 · State ManagementTopics · 5/7

React Query (TanStack Query)

Part of the Frontend Developer Roadmap

In short

API data isn't like a toggle or a form field. It lives on a server, goes stale and gets shared across screens. TanStack Query handles the caching, refetching and loading states.

A library specifically for server-state — fetching, caching, background-refetching and syncing API data — that eliminates most manual loading/error/cache logic.

The jaadu mascot reads a newspaper at breakfast as a fresh one flies in through the window.

Think of it like this

The morning newspaper

You read the paper lying on the table straight away, even if it's from this morning. When it gets old, a fresh one arrives without you asking. TanStack Query shows cached data instantly and quietly refetches in the background once it's stale.

Watch it happen

Cache first, network when needed

Posts
Query cache
API

key ["posts"]

1/6Posts mounts and asks the cache for ["posts"].

The big ideas

What you need to know

queryKey names the data

['posts', page] identifies a query. The same key anywhere in the app means the same cached data.

useQuery reads

It gives you data, error, isPending and more, so you stop writing loading state by hand.

useMutation writes

Use it for POST, PUT and DELETE, then invalidate related queries so lists refresh.

staleTime controls freshness

By default, data counts as stale right away. Set staleTime to skip refetches for a while.

See it in code

Walk through the code

tsx
1import { useQuery } from "@tanstack/react-query";2 3type Post = { id: number; title: string };4 5export function Posts() {6  const { data, isPending, error } = useQuery({7    queryKey: ["posts"],8    queryFn: (): Promise<Post[]> =>9      fetch("https://jsonplaceholder.typicode.com/posts").then((r) => r.json()),10    staleTime: 60_000,11  });12 13  if (isPending) return <p>Loading...</p>;14  if (error) return <p>Something went wrong</p>;15  return <ul>{data.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;16}

1/4useQuery is named by its key. Same key, same cached data.

Wrap the app in QueryClientProvider first. With a staleTime of 60 seconds, coming back to this screen within a minute shows the cache without a new request.

Save yourself the pain

Common mistakes

Oops

Copying query data into useState or a Redux store.

Do this instead

The cache already is your state. Read data straight from useQuery, or you'll end up showing stale copies.

Oops

Using the key ['posts'] for every page of posts.

Do this instead

Put every input in the key, like ['posts', page]. Otherwise different pages fight over one cache entry.

Your mission

Swap useEffect for useQuery

~30 min

Quick check

Two components on the same screen call useQuery with queryKey ['user', 7]. How many network requests go out?

Go deeper

  1. 1Replace a manual `useEffect` + `fetch` pattern with `useQuery`.
  2. 2Learn how automatic caching and refetch-on-focus change your app's behavior.
  3. 3Use `useMutation` for POST/PUT/DELETE requests with cache invalidation.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge