Integrating Axios with React Query

Integrating Axios with React Query (TanStack Query) provides a robust solution for server-state management and asynchronous data fetching in React applications. This guide demonstrates how to configure Axios instances, write clean query functions, handle parameters dynamically, execute mutations for data updates, and manage errors effectively within the TanStack Query lifecycle.

Setting Up an Axios Instance

Creating a pre-configured Axios instance ensures consistent base URLs, headers, and interceptors across your application.

// apiClient.js
import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'https://api.example.com/v1',
  headers: {
    'Content-Type': 'application/json',
  },
});

export default apiClient;

Basic Data Fetching with useQuery

React Query's queryFn requires a function that returns a Promise. When using Axios, return response.data so the query receives the direct payload rather than the entire Axios response object.

// useGetUsers.js
import { useQuery } from '@tanstack/react-query';
import apiClient from './apiClient';

const fetchUsers = async () => {
  const response = await apiClient.get('/users');
  return response.data;
};

export const useGetUsers = () => {
  return useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
};

Handling Dynamic Parameters

To pass dynamic parameters (such as resource IDs or filters) to an Axios request, pass them through the query function or read them directly from the query key context.

// useGetUserById.js
import { useQuery } from '@tanstack/react-query';
import apiClient from './apiClient';

const fetchUserById = async ({ queryKey }) => {
  const [_key, userId] = queryKey;
  const response = await apiClient.get(`/users/${userId}`);
  return response.data;
};

export const useGetUserById = (userId) => {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: fetchUserById,
    enabled: Boolean(userId), // Prevents execution if userId is undefined
  });
};

Executing Mutations with useMutation

For POST, PUT, PATCH, and DELETE requests, wrap Axios calls inside the mutationFn of the useMutation hook.

// useCreateUser.js
import { useMutation, useQueryClient } from '@tanstack/react-query';
import apiClient from './apiClient';

const createUser = async (newUserData) => {
  const response = await apiClient.post('/users', newUserData);
  return response.data;
};

export const useCreateUser = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      // Invalidate and refetch users query on successful creation
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
};

Error Handling

Axios automatically throws an error for HTTP status codes outside the 2xx range, which React Query catches seamlessly. You can access the structured Axios error inside your components:

const { data, error, isError, isLoading } = useGetUsers();

if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.response?.data?.message || error.message}</div>;