How to Implement React Query in React

This article provides a practical, step-by-step guide on how to integrate and use React Query (now known as TanStack Query) in a React application. You will learn how to install the package, set up the global query provider, fetch data using queries, and handle data mutations to keep your server state synchronized with your user interface.


Step 1: Install React Query

To get started, install the official TanStack Query package using your preferred package manager. Run one of the following commands in your project terminal:

# Using npm
npm install @tanstack/react-query

# Using yarn
yarn add @tanstack/react-query

Step 2: Configure the QueryClientProvider

React Query requires a QueryClientProvider to wrap your application and manage the cache. You should set this up at the entry point of your React application (usually main.jsx or index.js).

import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import App from './App';

// Create a client instance
const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);

Step 3: Fetch Data Using useQuery

To fetch data, use the useQuery hook. This hook requires a unique queryKey to manage the cache and a queryFn (an asynchronous function that fetches the data).

Here is an example of fetching a list of users from a public API:

import { useQuery } from '@tanstack/react-query';

// Asynchronous fetch function
const fetchUsers = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  if (!response.ok) {
    throw new Error('Network response was not ok');
  }
  return response.json();
};

function UserList() {
  const { data, error, isLoading, isError } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });

  if (isLoading) {
    return <p>Loading users...</p>;
  }

  if (isError) {
    return <p>Error: {error.message}</p>;
  }

  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

export default UserList;

Step 4: Modify Data Using useMutation

To create, update, or delete data on the server, use the useMutation hook. You can combine it with useQueryClient to invalidate existing queries, forcing React Query to automatically refetch and display the latest data.

Here is an example of adding a new user:

import { useMutation, useQueryClient } from '@tanstack/react-query';

// Asynchronous mutation function
const createUser = async (newUserData) => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newUserData),
  });
  return response.json();
};

function AddUserButton() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      // Invalidate the 'users' cache to trigger a background refetch
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });

  const handleAddUser = () => {
    mutation.mutate({ name: 'Jane Doe' });
  };

  return (
    <div>
      <button onClick={handleAddUser} disabled={mutation.isPending}>
        {mutation.isPending ? 'Adding...' : 'Add User'}
      </button>
      {mutation.isError && <p>Error adding user: {mutation.error.message}</p>}
      {mutation.isSuccess && <p>User added successfully!</p>}
    </div>
  );
}

export default AddUserButton;