Frontend Development 17 min read

Comprehensive React Query Tutorial: Managing Server and Client State in React

This tutorial introduces React Query (TanStack Query), explains its motivation, demonstrates how to manage client and server state in React using hooks, Context, useReducer, and provides best practices for caching, optimistic updates, reducing duplicate requests, and performance optimization techniques.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Comprehensive React Query Tutorial: Managing Server and Client State in React

React Query (formerly TanStack Query) is described as the missing data‑fetching library for web applications, offering effortless fetching, caching, synchronizing, and updating of server state. It was created because most core web frameworks lack an opinionated way to handle data fetching, leading developers to build custom solutions or use generic state management libraries.

After version 4 the library expanded to other frameworks (Vue, Solid, Svelte) and was renamed TanStack Query, but this series focuses on its usage within React, referring to it as React Query.

Preface : In React development, state management is core. While many tools handle client‑side state, they are not optimized for server state. React Query was built to address server‑state management, and this series explores its best practices.

What is React state? A simple counter demo illustrates component state using useState . The example shows increment, decrement, and reset functions, and highlights that the count variable represents the state that drives UI updates.

const App = () => {
  const [count, setCount] = useState(0);

  const increment = () => setCount(currentCount => currentCount + 1);
  const decrement = () => setCount(currentCount => currentCount - 1);
  const reset = () => setCount(0);

  return (
Counter: {count}
+1
-1
Reset
);
};

For more complex scenarios, useReducer can be used to manage state transitions with explicit action types, providing a clearer pattern for larger state objects.

const initialState = { count: 0 };
const types = { INCREMENT: "increment", DECREMENT: "decrement", RESET: "reset" };

const reducer = (state, action) => {
  switch (action) {
    case types.INCREMENT:
      return { count: state.count + 1 };
    case types.DECREMENT:
      return { count: state.count - 1 };
    case types.RESET:
      return { count: 0 };
    default:
      throw new Error("This type does not exist");
  }
};

const AppWithReducer = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const increment = () => dispatch(types.INCREMENT);
  const decrement = () => dispatch(types.DECREMENT);
  const reset = () => dispatch(types.RESET);

  return (
Counter: {state.count}
+1
-1
Reset
);
};

Sharing State : React Context enables state sharing across components without prop drilling. A CountProvider component wraps the app, exposing count , increment , decrement , and reset via a context value. Consumers use useContext(CountContext) to access these values.

export const CountContext = createContext();
export const CountStore = () => {
  const [count, setCount] = useState(0);
  const increment = () => setCount(c => c + 1);
  const decrement = () => setCount(c => c - 1);
  const reset = () => setCount(0);
  return { count, increment, decrement, reset };
};

const CountProvider = ({ children }) => (
{...children}
);
export default CountProvider;

In index.js the app is rendered inside CountProvider , and App.js consumes the context to display and manipulate the counter.

import { useContext } from "react";
import { CountContext } from "./components/CountProvider";

function App() {
  const { count, increment, decrement, reset } = useContext(CountContext);
  return (
Counter: {count}
+1
-1
Reset
);
}
export default App;

Note: Updating any value in a Context causes all consuming components to re‑render, which can lead to unnecessary renders if only a subset of the state changes. Techniques such as splitting contexts, memoizing components, or using useMemo for the provided value can mitigate this.

Server vs. Client State : Global state in a React app often mixes server‑derived data (fetched from APIs) and client‑only data (UI state). Server state is asynchronous, remote, and may become stale; client state is synchronous, local, and temporary. Recognizing the distinction helps decide whether to use React Query or other tools like Redux, Zustand, or MobX.

Examples in the article illustrate a GlobalStore that manages theme selection ( selectedTheme – client state), server data ( serverData – server state), loading flags ( isLoadingData – derived state), and a toggle function. Adding more state (e.g., secondaryTheme ) demonstrates how Context can become cumbersome as the state surface grows.

Challenges with Global State : As more state variables are added, components may re‑render unnecessarily, and managing caching, optimistic updates, and error handling becomes complex.

Caching : Effective caching improves performance by reusing previously fetched data, updating caches in the background, handling expiration, garbage‑collecting stale entries, and optionally seeding caches with placeholder data.

Optimistic Updates (Mutations) : Instead of waiting for a server response, the UI is updated immediately to reflect the expected outcome, then the actual mutation is performed. If the mutation fails, the UI rolls back to the previous state.

Reducing Duplicate Requests : Prevent rapid repeated clicks from firing multiple identical requests by disabling buttons during loading or debouncing actions.

Performance Optimizations : Techniques such as lazy loading, infinite scrolling, and pagination reduce the amount of data fetched at once, improving responsiveness.

The article concludes by emphasizing that React Query (TanStack Query) abstracts these complexities, offering a concise API that handles caching, background updates, optimistic mutations, and more, allowing developers to become masters of server‑state management.

frontendstate managementreactuseReducerContext APIReact QueryTanStack Query
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.