Optimizing Axios Performance in React
Optimizing Axios in React applications is essential for improving page load times, reducing server load, and ensuring a smooth user experience. This article provides a practical guide on how to streamline your Axios configuration through custom instances, request interceptors, cancellation tokens, response caching, and integration with modern state management tools.
1. Create a Custom Axios Instance
Instead of importing the global Axios library across multiple files, create a reusable, customized instance. This centralizes your configuration, making it easier to manage base URLs, timeouts, and headers.
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000, // 10 seconds timeout
headers: {
'Content-Type': 'application/json',
},
});
export default api;2. Utilize Request and Response Interceptors
Interceptors allow you to run your code or modify requests and
responses before they are handled by then or
catch. This is ideal for automatically attaching
authorization tokens or globally handling HTTP errors.
// Attaching authorization token automatically
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Global error handling
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
// Redirect to login or refresh token
}
return Promise.reject(error);
}
);3. Implement Request Cancellation
To prevent memory leaks and unnecessary network usage, cancel pending
requests when a component unmounts or when a dependency changes. Axios
supports this using the native AbortController.
import React, { useEffect, useState } from 'react';
import api from './api';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const controller = new AbortController();
api.get(`/users/${userId}`, { signal: controller.signal })
.then((res) => setUser(res.data))
.catch((err) => {
if (axios.isCancel(err)) {
console.log('Request canceled');
} else {
// Handle actual error
}
});
// Cancel the request if the component unmounts or userId changes
return () => {
controller.abort();
};
}, [userId]);
return <div>{user ? user.name : 'Loading...'}</div>;
}4. Implement Response Caching
Repeatedly fetching the exact same data drains bandwidth. You can implement a simple in-memory cache to store API responses and serve them instantly on subsequent requests.
const cache = new Map();
export const getCachedData = async (url) => {
if (cache.has(url)) {
return cache.get(url);
}
const response = await api.get(url);
cache.set(url, response.data);
return response.data;
};5. Leverage TanStack Query (React Query)
The most robust way to optimize Axios in React is by combining it with a data-fetching library like TanStack Query (formerly React Query). It handles caching, background updates, deduplication of requests, and garbage collection automatically.
import { useQuery } from '@tanstack/react-query';
import api from './api';
const fetchUserData = async (userId) => {
const { data } = await api.get(`/users/${userId}`);
return data;
};
function UserComponent({ userId }) {
const { data, error, isLoading } = useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUserData(userId),
staleTime: 5 * 60 * 1000, // Keep data fresh for 5 minutes
});
if (isLoading) return <span>Loading...</span>;
if (error) return <span>Error: {error.message}</span>;
return <div>{data.name}</div>;
}