Detailed explanation
Server state is data whose authoritative source is a backend; React usually holds a fetched snapshot of it. Examples include users, products, orders, and account balances. Client state is owned by the frontend and represents interactions or presentation, such as an open modal, an active tab, or an unsaved form draft. The distinction is about ownership and lifecycle, not simply where a value is stored: putting fetched products into useState does not make them client-owned data.
Server state requires coordination with an external system. Requests take time, can fail, and may return data that another user changes immediately afterwards. A React application therefore needs policies for loading, errors, caching, freshness, concurrent requests, and updates after mutations. TanStack Query, SWR, and RTK Query provide server-state utilities; framework loaders can coordinate route-level fetching, with caching capabilities varying by framework. In TanStack Query, query keys identify cached results and must include relevant inputs, such as filters and page numbers. staleTime controls how long data is considered fresh, while gcTime controls when inactive cached data is removed; becoming stale does not itself trigger an immediate request.
Client state usually needs simpler update rules. Use useState for local values, useReducer for related transitions, and Context or a shared store when multiple components need access. Put shareable navigation state, such as filters or pagination, in URL search parameters. Client state can also persist in localStorage or synchronise across tabs, so it is not necessarily temporary or always current. React state updates are scheduled rather than immediately mutating the current render's values.
For example, a product list is server state, the selected category is client or URL state, and an edited product form is a local draft until saved. After a successful save, update or invalidate relevant cached queries; optimistic updates may require rollback on failure. Interviewers look for this separation and its consistency trade-offs. Fetching in useEffect is not inherently wrong, but manually rebuilding caching, race handling, and refetching across components is often unnecessary and error-prone.
Key points to remember
- Classify state by its authoritative owner, not by the hook or store holding it.
- Server state needs freshness, request lifecycle, caching, and mutation-consistency policies.
- Include every input affecting fetched results in the query key.
- Keep UI state local unless sharing, persistence, or navigation requirements justify another location.
- Treat editable drafts separately from cached server records and reconcile them when saving.
Common mistakes candidates make
- Assuming all data in Redux or useState is client state, or that Redux cannot manage server state through RTK Query.
- Copying query results into another store without a clear purpose, creating competing copies that can diverge.
- Claiming client state is always synchronous and temporary, or that staleTime automatically schedules a refetch.
Likely follow-up questions
What happens when you invalidate a query after a mutation?
In TanStack Query, matching queries are marked stale, overriding their configured staleTime. By default, active matching queries refetch in the background; inactive ones are not immediately refetched.
Should server data ever be copied into local form state?
Yes, when the form needs an independently editable draft rather than a live view of the cached record. Decide how background updates affect dirty fields so a refetch does not silently overwrite the user's edits.
Can client state affect server state fetching?
Yes: a selected filter or page number can determine which server data is requested. Include those values in the query key so different result sets have distinct cache entries.
Interview tip: Start with ownership, contrast the lifecycle requirements, and finish with one example separating fetched data, UI controls, and an editable draft.
