[{"data":1,"prerenderedAt":131},["ShallowReactive",2],{"qa-\u002Freact\u002Fstate-management\u002Fasync-state-react-query":3},{"page":4,"siblings":115,"blog":128},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":105,"related":106,"seo":107,"seoDescription":108,"stem":109,"subtopic":110,"topic":111,"topicSlug":112,"updated":113,"__hash__":114},"qa\u002Freact\u002Fstate-management\u002Fasync-state-react-query.md","Async State React Query",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","React","react",{},true,4,"\u002Freact\u002Fstate-management\u002Fasync-state-react-query",[23,28,32,36,40,44,48,52,57,61,65,69,73,77,81,85,89,93,97,101],{"id":24,"difficulty":25,"q":26,"a":27},"what-is-react-query","easy","What is TanStack Query (React Query) and what problem does it solve?","**TanStack Query** (formerly React Query) is a library for managing\n**server state** in React apps. It solves the boilerplate problem of\nusing `useState` + `useEffect` to fetch data: no more manual loading\nflags, error states, cache invalidation, or race-condition handling.\n\n```jsx\n\u002F\u002F Without React Query — manual state machine\nconst [data, setData] = useState(null)\nconst [loading, setLoading] = useState(false)\nconst [error, setError] = useState(null)\n\nuseEffect(() => {\n  setLoading(true)\n  fetch('\u002Fapi\u002Fuser')\n    .then(r => r.json())\n    .then(d => { setData(d); setLoading(false) })\n    .catch(e => { setError(e); setLoading(false) })\n}, [])\n\n\u002F\u002F With React Query — three lines replace the whole block\nconst { data, isLoading, error } = useQuery({\n  queryKey: ['user'],\n  queryFn: () => fetch('\u002Fapi\u002Fuser').then(r => r.json()),\n})\n```\n\nReact Query also handles caching, background refetching, deduplication of\nconcurrent requests, and retry logic — all out of the box.\n\n**Rule of thumb:** if the data lives on a server and you want it fresh in\nthe UI, React Query is the right tool; it is not for ephemeral UI state\nlike modal toggles.\n",{"id":29,"difficulty":25,"q":30,"a":31},"usequery-basics","What are the three required \u002F most-used options for useQuery?","`useQuery` needs at minimum a **queryKey** and a **queryFn**. The third\ncommon option is `staleTime`.\n\n```jsx\nimport { useQuery } from '@tanstack\u002Freact-query'\n\nfunction UserProfile({ userId }) {\n  const { data, isLoading, isError, error } = useQuery({\n    queryKey: ['user', userId],      \u002F\u002F cache key — array form\n    queryFn: () =>\n      fetch(`\u002Fapi\u002Fusers\u002F${userId}`).then(r => r.json()), \u002F\u002F async fetcher\n    staleTime: 1000 * 60 * 5,        \u002F\u002F treat data as fresh for 5 min\n  })\n\n  if (isLoading) return \u003CSpinner \u002F>\n  if (isError)   return \u003Cp>{error.message}\u003C\u002Fp>\n  return \u003Ch1>{data.name}\u003C\u002Fh1>\n}\n```\n\nThe hook returns many flags — `isPending`, `isFetching`, `isSuccess`,\n`isError`, `isStale` — plus `data`, `error`, and `refetch`. `isLoading`\nis true only on the very first fetch (no cached data + fetching); use\n`isFetching` to show a background-refetch spinner.\n\n**Rule of thumb:** always destructure only what you need; the full return\nobject has ~25 properties.\n",{"id":33,"difficulty":25,"q":34,"a":35},"query-keys-importance","Why do query keys matter in React Query?","**Query keys** are the primary key for the query cache. Every unique key\nmaps to its own cache entry. React Query uses them to deduplicate\nin-flight requests, invalidate targeted entries after mutations, and\nre-run queries when the key changes.\n\n```jsx\n\u002F\u002F Different keys → different cache entries\nuseQuery({ queryKey: ['user', 1], queryFn: ... }) \u002F\u002F cache: user\u002F1\nuseQuery({ queryKey: ['user', 2], queryFn: ... }) \u002F\u002F cache: user\u002F2\n\n\u002F\u002F Key changes when userId changes → automatic re-fetch\nuseQuery({ queryKey: ['user', userId], queryFn: fetchUser })\n\n\u002F\u002F Invalidate all 'user' queries after an update\nqueryClient.invalidateQueries({ queryKey: ['user'] })\n```\n\nKeys are serialized deeply (arrays and objects both work), so\n`['users', { status: 'active' }]` and `['users', { status: 'inactive' }]`\nare independent cache slots. Treat keys like a URL: they should\nuniquely and fully describe the data being fetched.\n\n**Rule of thumb:** always include every variable the query depends on\n(IDs, filters, page numbers) in the key — never leave a dependency out.\n",{"id":37,"difficulty":14,"q":38,"a":39},"stale-time-vs-cache-time","What is the difference between staleTime and gcTime (cacheTime)?","**`staleTime`** controls how long fetched data is considered fresh.\nDuring that window React Query will serve cached data without\nre-fetching. Default is `0` (immediately stale).\n\n**`gcTime`** (formerly `cacheTime`) controls how long **unused** cached\ndata is kept in memory before garbage collection. Default is 5 minutes.\n\n```jsx\nuseQuery({\n  queryKey: ['posts'],\n  queryFn: fetchPosts,\n  staleTime: 1000 * 60,      \u002F\u002F data is fresh for 1 min\n  gcTime: 1000 * 60 * 10,    \u002F\u002F cache kept 10 min after last observer\n})\n\u002F\u002F 0–60 s:  component mounts → serves cache, no network request\n\u002F\u002F 60+ s:   data is stale → re-fetch in background on next mount\u002Ffocus\n\u002F\u002F unmount: cache kept 10 min then collected\n```\n\nA high `staleTime` with a long `gcTime` means near-instant navigation\nbetween pages without spinners, while the data stays reasonably fresh.\n\n**Rule of thumb:** `staleTime` = \"how often can I tolerate stale data\";\n`gcTime` = \"how long should I cache data after nobody is watching it.\"\n",{"id":41,"difficulty":14,"q":42,"a":43},"background-refetching","When does React Query automatically refetch data in the background?","React Query triggers a background refetch — silently updating stale data\nwithout showing a spinner — in four situations by default:\n\n```jsx\nuseQuery({\n  queryKey: ['todos'],\n  queryFn: fetchTodos,\n  \u002F\u002F all true by default:\n  refetchOnWindowFocus: true,     \u002F\u002F tab gets focus again\n  refetchOnMount: true,           \u002F\u002F new component instance mounts\n  refetchOnReconnect: true,       \u002F\u002F network comes back online\n  staleTime: 0,                   \u002F\u002F data is immediately stale\n})\n```\n\nDuring a background refetch `isFetching` is `true` but `isLoading`\nremains `false` (cached data still displayed). This lets you show a\nsubtle \"refreshing\" indicator without blanking the screen.\n\nYou can also trigger refetches manually via `refetch()` or via\n`queryClient.invalidateQueries({ queryKey: ['todos'] })`.\n\n**Rule of thumb:** increase `staleTime` to reduce unnecessary network\ntraffic; set `refetchOnWindowFocus: false` in forms or dashboards where\na mid-edit refresh would confuse users.\n",{"id":45,"difficulty":25,"q":46,"a":47},"loading-error-success-states","How do you handle loading, error, and success states with useQuery?","`useQuery` returns boolean flags for every state in the request lifecycle.\nUse them to render the right UI slice.\n\n```jsx\nconst { data, isLoading, isError, error, isSuccess } = useQuery({\n  queryKey: ['profile'],\n  queryFn: fetchProfile,\n})\n\n\u002F\u002F isLoading: true only on first fetch (no cached data)\nif (isLoading) return \u003CSkeleton \u002F>\n\n\u002F\u002F isError: true when queryFn threw or rejected\nif (isError) return \u003CAlert message={error.message} \u002F>\n\n\u002F\u002F isSuccess: true once data is available (even stale)\nreturn \u003CProfile user={data} \u002F>\n```\n\nFor background refreshes where you want to keep showing old data while\nnew data loads, check `isFetching` alongside `isSuccess`.\n\n**Rule of thumb:** check `isLoading` for the empty-state skeleton and\n`isError` for the error boundary — never check `!data` as a loading\nproxy, because stale data can exist alongside a fresh fetch.\n",{"id":49,"difficulty":14,"q":50,"a":51},"usemutation-basics","How does useMutation work and what callbacks does it provide?","**`useMutation`** handles write operations (POST, PUT, DELETE). Unlike\n`useQuery` it does not run automatically — you call `mutate()` or\n`mutateAsync()` explicitly.\n\n```jsx\nconst mutation = useMutation({\n  mutationFn: (newTodo) =>\n    fetch('\u002Fapi\u002Ftodos', {\n      method: 'POST',\n      body: JSON.stringify(newTodo),\n    }).then(r => r.json()),\n\n  onSuccess: (data, variables, context) => {\n    \u002F\u002F data = server response, variables = what you passed to mutate()\n    queryClient.invalidateQueries({ queryKey: ['todos'] })\n  },\n  onError: (error, variables, context) => {\n    console.error('Mutation failed:', error.message)\n  },\n  onSettled: () => {\n    \u002F\u002F runs after onSuccess OR onError — good for cleanup\n  },\n})\n\n\u002F\u002F trigger it\nmutation.mutate({ title: 'Buy milk' })\n```\n\n`mutation.status` cycles through `idle → pending → success | error`.\nUse `mutation.isPending` to disable the submit button.\n\n**Rule of thumb:** always invalidate or update the relevant query cache\nin `onSuccess` so the UI stays in sync with the server.\n",{"id":53,"difficulty":54,"q":55,"a":56},"optimistic-updates","hard","How do you implement optimistic updates with useMutation?","**Optimistic updates** apply the expected change to the cache immediately\n— before the server responds — so the UI feels instant. If the mutation\nfails you roll back using the snapshot saved in `onMutate`.\n\n```jsx\nconst queryClient = useQueryClient()\n\nconst toggleTodo = useMutation({\n  mutationFn: (todo) =>\n    fetch(`\u002Fapi\u002Ftodos\u002F${todo.id}`, { method: 'PATCH',\n      body: JSON.stringify({ done: !todo.done }) }).then(r => r.json()),\n\n  onMutate: async (todo) => {\n    \u002F\u002F 1. cancel any in-flight refetch (avoids overwriting our optimistic data)\n    await queryClient.cancelQueries({ queryKey: ['todos'] })\n\n    \u002F\u002F 2. snapshot the previous value\n    const previous = queryClient.getQueryData(['todos'])\n\n    \u002F\u002F 3. optimistically update the cache\n    queryClient.setQueryData(['todos'], (old) =>\n      old.map(t => t.id === todo.id ? { ...t, done: !t.done } : t)\n    )\n\n    return { previous }  \u002F\u002F returned as \"context\"\n  },\n\n  onError: (_err, _todo, context) => {\n    \u002F\u002F 4. roll back on failure\n    queryClient.setQueryData(['todos'], context.previous)\n  },\n\n  onSettled: () => {\n    \u002F\u002F 5. always refetch to sync with server truth\n    queryClient.invalidateQueries({ queryKey: ['todos'] })\n  },\n})\n```\n\nThe pattern — cancel → snapshot → optimistic write → rollback on error →\ninvalidate on settle — is the canonical React Query optimistic workflow.\n\n**Rule of thumb:** use optimistic updates for high-frequency interactions\n(toggles, likes, reorder) where a 200–500 ms spinner would feel sluggish.\n",{"id":58,"difficulty":14,"q":59,"a":60},"query-invalidation","How does query invalidation work and why is it preferred over manual cache writes?","**Query invalidation** marks a cached query as stale so that React Query\nrefetches it the next time an active observer is present (or immediately\nif one is currently mounted).\n\n```jsx\n\u002F\u002F After creating a new post, invalidate the list\nconst mutation = useMutation({\n  mutationFn: createPost,\n  onSuccess: () => {\n    \u002F\u002F exact: false (default) — invalidates 'posts' and all sub-keys\n    queryClient.invalidateQueries({ queryKey: ['posts'] })\n\n    \u002F\u002F exact: true — invalidates only this exact key\n    queryClient.invalidateQueries({ queryKey: ['posts', 'list'], exact: true })\n  },\n})\n```\n\nInvalidation is preferred over `setQueryData` for list-after-create\nbecause the server may add timestamps, IDs, or derived fields that the\nclient can't predict. Re-fetching gets you the canonical truth.\n\n**Rule of thumb:** invalidate for create\u002Fdelete operations (server adds\ndata you don't have); use `setQueryData` only for update operations where\nyou already have the complete new object.\n",{"id":62,"difficulty":14,"q":63,"a":64},"dependent-queries","How do you fetch query B only after query A has completed (dependent queries)?","Use the **`enabled`** option. When `enabled` is `false` the query is\nsuspended — it will not fetch until the expression becomes truthy.\n\n```jsx\nfunction UserOrders({ userId }) {\n  \u002F\u002F Step 1: fetch the user\n  const { data: user } = useQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  })\n\n  \u002F\u002F Step 2: fetch orders only once we have user.accountId\n  const { data: orders } = useQuery({\n    queryKey: ['orders', user?.accountId],\n    queryFn: () => fetchOrders(user.accountId),\n    enabled: !!user?.accountId,  \u002F\u002F waits for accountId to exist\n  })\n\n  return \u003COrderList orders={orders} \u002F>\n}\n```\n\n`enabled` accepts any expression — `!!id`, `isSuccess`, a feature flag\n— and React Query will start the query the moment the value becomes\ntruthy.\n\n**Rule of thumb:** always guard with `!!value` not just `value`, so that\nempty strings and `0` don't accidentally enable a query.\n",{"id":66,"difficulty":54,"q":67,"a":68},"infinite-queries","How does useInfiniteQuery work for paginated or infinite-scroll data?","**`useInfiniteQuery`** manages a list of pages. Each page is fetched via\n`queryFn` which receives `pageParam` (the cursor\u002Fpage number for that\npage). `getNextPageParam` derives the next cursor from the last page.\n\n```jsx\nconst {\n  data,           \u002F\u002F { pages: [...], pageParams: [...] }\n  fetchNextPage,\n  hasNextPage,\n  isFetchingNextPage,\n} = useInfiniteQuery({\n  queryKey: ['posts'],\n  queryFn: ({ pageParam = 0 }) =>\n    fetch(`\u002Fapi\u002Fposts?cursor=${pageParam}`).then(r => r.json()),\n\n  getNextPageParam: (lastPage) =>\n    lastPage.nextCursor ?? undefined, \u002F\u002F undefined stops pagination\n\n  initialPageParam: 0,               \u002F\u002F v5 required option\n})\n\n\u002F\u002F Flatten all pages for rendering\nconst posts = data?.pages.flatMap(p => p.items) ?? []\n```\n\nCall `fetchNextPage()` from an \"Load more\" button or an intersection\nobserver at the bottom of the list.\n\n**Rule of thumb:** `useInfiniteQuery` is for cursor-based or\noffset-based lists; for discrete page navigation (page 1 \u002F 2 \u002F 3\nbuttons) use a plain `useQuery` with the page number in the key.\n",{"id":70,"difficulty":25,"q":71,"a":72},"queryclient-setup","How do you set up QueryClient and QueryClientProvider?","Create a **`QueryClient`** instance once at app startup and wrap the\ntree with **`QueryClientProvider`**. Every hook inside the tree shares\nthat client.\n\n```jsx\n\u002F\u002F main.tsx \u002F _app.tsx\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { ReactQueryDevtools } from '@tanstack\u002Freact-query-devtools'\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60,      \u002F\u002F global 1-min freshness\n      retry: 2,                   \u002F\u002F retry failed queries twice\n    },\n  },\n})\n\nexport default function App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CRouter \u002F>\n      \u003CReactQueryDevtools initialIsOpen={false} \u002F> {\u002F* dev-only panel *\u002F}\n    \u003C\u002FQueryClientProvider>\n  )\n}\n```\n\n`QueryClient` is created outside the component so it is not re-created\non every render. In tests, create a fresh `QueryClient` per test to\nprevent shared state between cases.\n\n**Rule of thumb:** never create the `QueryClient` inside a component —\nit would reset the cache on every render.\n",{"id":74,"difficulty":14,"q":75,"a":76},"prefetching","What is prefetchQuery and when would you use it?","**`prefetchQuery`** populates the cache proactively — before the\ncomponent that needs the data has mounted. This eliminates the loading\nstate for routes the user is likely to visit next.\n\n```jsx\nconst queryClient = useQueryClient()\n\n\u002F\u002F On mouse-enter of a \"User Profile\" link\nfunction UserLink({ userId }) {\n  const handleMouseEnter = () => {\n    queryClient.prefetchQuery({\n      queryKey: ['user', userId],\n      queryFn: () => fetchUser(userId),\n      staleTime: 1000 * 60 * 5,  \u002F\u002F don't prefetch if cached and fresh\n    })\n  }\n  return \u003CLink to={`\u002Fusers\u002F${userId}`} onMouseEnter={handleMouseEnter}>...\u003C\u002FLink>\n}\n```\n\nYou can also call `prefetchQuery` in a route loader (React Router v6+,\nNext.js `getServerSideProps`, TanStack Router) to fetch data on the\nserver before the page renders.\n\n**Rule of thumb:** prefetch on hover\u002Fintent; do not prefetch every\npossible route eagerly or you'll waste bandwidth.\n",{"id":78,"difficulty":14,"q":79,"a":80},"server-vs-client-state","What is the difference between server state and client state, and why does it matter?","**Server state** is data that lives on a remote server and is fetched\nasynchronously — it can change outside your app (another user edits it,\na cron job updates it). **Client state** is ephemeral UI state that only\nyour app owns: modal open\u002Fclosed, form draft, selected tab.\n\n```jsx\n\u002F\u002F Client state — lives only in the browser\nconst [isModalOpen, setIsModalOpen] = useState(false)\n\n\u002F\u002F Server state — a snapshot of remote data\nconst { data: todos } = useQuery({\n  queryKey: ['todos'],\n  queryFn: fetchTodos,\n})\n\u002F\u002F \"todos\" can become stale while the user is on the page\n```\n\nServer state has extra concerns client state doesn't: caching, staleness,\nbackground synchronisation, deduplication, pagination. Managing it with\n`useState` forces you to re-implement all of these manually.\n\n**Rule of thumb:** use `useState`\u002F`useReducer`\u002FZustand for client state;\nuse React Query (or SWR) for server state — keep the two layers separate.\n",{"id":82,"difficulty":14,"q":83,"a":84},"pagination-patterns","How do you implement paginated data fetching with useQuery?","Include the **page number** in the query key so each page has its own\ncache entry. Use **`placeholderData: keepPreviousData`** (v5) to avoid\nblanking the UI between pages.\n\n```jsx\nimport { useQuery, keepPreviousData } from '@tanstack\u002Freact-query'\n\nfunction TodoList() {\n  const [page, setPage] = useState(1)\n\n  const { data, isPlaceholderData } = useQuery({\n    queryKey: ['todos', page],\n    queryFn: () => fetchTodos(page),\n    placeholderData: keepPreviousData, \u002F\u002F keep showing page N while N+1 loads\n  })\n\n  return (\n    \u003C>\n      {isPlaceholderData && \u003CLoadingBar \u002F>}\n      \u003CList items={data?.items} \u002F>\n      \u003Cbutton\n        onClick={() => setPage(p => p - 1)}\n        disabled={page === 1}>Prev\u003C\u002Fbutton>\n      \u003Cbutton\n        onClick={() => setPage(p => p + 1)}\n        disabled={!data?.hasMore}>Next\u003C\u002Fbutton>\n    \u003C\u002F>\n  )\n}\n```\n\nReact Query can also prefetch the next page when `hasMore` is true,\nmaking forward navigation feel instant.\n\n**Rule of thumb:** always put pagination state in the query key; never\nmutate an external variable that the key doesn't reflect.\n",{"id":86,"difficulty":14,"q":87,"a":88},"react-query-vs-redux","When should you use React Query instead of Redux for data fetching?","Use **React Query** when data originates from a server and your main\nconcerns are caching, staleness, and background sync. Use **Redux** when\nyou have complex cross-cutting client state (undo\u002Fredo, multi-step\nworkflows, real-time collaborative state shared between many components).\n\n```jsx\n\u002F\u002F React Query — server data, auto-caching, zero boilerplate\nconst { data: user } = useQuery({ queryKey: ['user'], queryFn: getUser })\n\n\u002F\u002F Redux (RTK Query) — same fetching capability, but adds global client state\nconst user = useSelector(selectUser)       \u002F\u002F cross-slice derivations\ndispatch(userSlice.actions.setRole('admin')) \u002F\u002F synchronous client mutation\n```\n\nMany teams use both: React Query for server state, Zustand or Redux for\nthe small amount of true client state (auth session, UI preferences).\nRTK Query (Redux Toolkit's built-in fetching solution) is an alternative\nif you're already invested in Redux.\n\n**Rule of thumb:** reach for React Query first; only add Redux if you\ngenuinely need global synchronous state across many parts of the app.\n",{"id":90,"difficulty":14,"q":91,"a":92},"react-query-vs-swr","How does React Query compare to SWR?","Both **SWR** (Vercel) and **React Query** implement stale-while-revalidate\ncaching. The differences are in scope and power:\n\n```jsx\n\u002F\u002F SWR — minimal, opinionated API\nconst { data, error, isLoading } = useSWR('\u002Fapi\u002Fuser', fetcher)\n\n\u002F\u002F React Query — richer API: mutations, infinite queries, optimistic updates\nconst { data, isLoading } = useQuery({ queryKey: ['\u002Fapi\u002Fuser'], queryFn: fetcher })\nconst mutation = useMutation({ mutationFn: updateUser })\n```\n\nSWR is simpler — almost no configuration, tiny bundle. React Query has\nmore features: `useMutation` with full lifecycle callbacks, `useInfiniteQuery`,\n`QueryClient` for server-side prefetching, advanced `select` transforms,\nand the excellent DevTools panel.\n\n**Rule of thumb:** SWR for small apps that only need fetch-and-cache;\nReact Query when you also need mutations, optimistic updates, pagination,\nor fine-grained cache control.\n",{"id":94,"difficulty":54,"q":95,"a":96},"suspense-mode","How does React Query integrate with React Suspense?","Pass **`useSuspenseQuery`** (v5) instead of `useQuery` to let React\nSuspense handle the loading state declaratively. The component suspends\nwhile data loads; wrap it in `\u003CSuspense>` with a fallback.\n\n```jsx\n\u002F\u002F v5 API\nimport { useSuspenseQuery } from '@tanstack\u002Freact-query'\n\nfunction UserProfile({ userId }) {\n  \u002F\u002F throws a Promise while loading → Suspense catches it\n  const { data } = useSuspenseQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  })\n  \u002F\u002F data is ALWAYS defined here — no isLoading check needed\n  return \u003Ch1>{data.name}\u003C\u002Fh1>\n}\n\n\u002F\u002F Parent\nfunction Page() {\n  return (\n    \u003CErrorBoundary fallback={\u003CErrorPage \u002F>}>\n      \u003CSuspense fallback={\u003CSkeleton \u002F>}>\n        \u003CUserProfile userId={1} \u002F>\n      \u003C\u002FSuspense>\n    \u003C\u002FErrorBoundary>\n  )\n}\n```\n\nError states bubble to the nearest `\u003CErrorBoundary>` instead of being\nreturned as a flag. This moves loading and error handling out of the\ndata component and into the layout layer.\n\n**Rule of thumb:** use Suspense mode when you want co-located data\nfetching with parent-controlled loading UI; keep the imperative\n`isLoading` approach in forms and components that handle their own\nerror display.\n",{"id":98,"difficulty":54,"q":99,"a":100},"ssr-hydration","How do you use React Query with SSR (e.g., Next.js) and avoid hydration mismatches?","Use **`HydrationBoundary`** with `dehydrate` to pass the server-fetched\ncache to the client, avoiding a client-side refetch on first render.\n\n```jsx\n\u002F\u002F app\u002Fusers\u002Fpage.tsx (Next.js App Router)\nimport { dehydrate, HydrationBoundary, QueryClient }\n  from '@tanstack\u002Freact-query'\n\nexport default async function UsersPage() {\n  const queryClient = new QueryClient()\n\n  \u002F\u002F prefetch on the server\n  await queryClient.prefetchQuery({\n    queryKey: ['users'],\n    queryFn: fetchUsers,\n  })\n\n  return (\n    \u002F\u002F serialise the cache and send it to the client\n    \u003CHydrationBoundary state={dehydrate(queryClient)}>\n      \u003CUserList \u002F>   {\u002F* useQuery(['users']) hits the cache immediately *\u002F}\n    \u003C\u002FHydrationBoundary>\n  )\n}\n```\n\nOn the client, React Query reads the dehydrated state and populates its\ncache before components mount, so they render with data immediately and\nno hydration mismatch occurs.\n\n**Rule of thumb:** always create a fresh `QueryClient` per request on the\nserver — never share the same instance across requests or you'll leak data\nbetween users.\n",{"id":102,"difficulty":14,"q":103,"a":104},"polling-refetch-interval","How do you poll an endpoint on a fixed interval with React Query?","Set **`refetchInterval`** to a millisecond value. React Query will refetch\nthe query on that interval while the component is mounted. Set\n`refetchIntervalInBackground: true` to keep polling even when the tab is\nnot focused.\n\n```jsx\nconst { data: jobStatus } = useQuery({\n  queryKey: ['job', jobId],\n  queryFn: () => fetchJobStatus(jobId),\n  refetchInterval: 3000,               \u002F\u002F poll every 3 seconds\n  refetchIntervalInBackground: false,  \u002F\u002F pause when tab is hidden (default)\n})\n\n\u002F\u002F Dynamic interval — stop polling once the job is done\nconst { data } = useQuery({\n  queryKey: ['job', jobId],\n  queryFn: () => fetchJobStatus(jobId),\n  refetchInterval: (query) => {\n    \u002F\u002F return false to stop; return ms to continue\n    return query.state.data?.status === 'done' ? false : 2000\n  },\n})\n```\n\nThe function form of `refetchInterval` receives the current query object,\nletting you implement \"poll until done\" patterns without extra state.\n\n**Rule of thumb:** always use the function form when you want to stop\npolling on a condition — a fixed interval that never stops wastes network\non completed resources.\n",20,null,{"description":11},"React Query interview questions — useQuery, useMutation, query keys, caching, stale time, refetching, optimistic updates, and async state management patterns.","react\u002Fstate-management\u002Fasync-state-react-query","Async State & React Query","State Management","state-management","2026-06-24","iVoG3RXlvfrXcn1S9vuDDXabHtP3hZ0NtPwLeckGYTA",[116,120,123,127],{"subtopic":117,"path":118,"order":119},"Redux Toolkit","\u002Freact\u002Fstate-management\u002Fredux-toolkit",1,{"subtopic":121,"path":122,"order":12},"Zustand","\u002Freact\u002Fstate-management\u002Fzustand",{"subtopic":124,"path":125,"order":126},"Context vs Redux","\u002Freact\u002Fstate-management\u002Fcontext-vs-redux",3,{"subtopic":110,"path":21,"order":20},{"path":129,"title":130},"\u002Fblog\u002Freact-async-state-react-query-guide","Async State & React Query — Complete React Interview Guide",1782244101209]