Enhancing React Performance with Interceptors and Middleware


Introduction
Optimizing performance and eliminating repetitive code are critical for developing scalable React apps. One of the most effective approaches is to use interceptors and middleware to handle API calls, authentication, error handling, and logging centrally. In this blog post, we'll look at how to use Axios interceptors with Redux Toolkit Middleware to optimize network requests and state management in React apps.
Understanding Interceptors in React
Interceptors allow you to modify requests and responses globally before they are handled by the .then()
or .catch()
block.
Why Use Interceptors?
Centralized API request handling
Automatic token addition for authentication
Global error handling and logging
Response transformation
Request retry mechanism for transient failures
Setting Up Axios Interceptors
Installing Axios
First, install Axios if you haven’t already:
npm install axios
Creating an Axios Instance with Interceptors
Create a utility file to configure Axios interceptors:
// src/utils/apiClient.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
});
// Request Interceptor
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response Interceptor
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response) {
if (error.response.status === 401) {
localStorage.removeItem('authToken');
window.location.href = '/login';
} else if (error.response.status >= 500) {
console.error('Server error:', error.response);
}
}
return Promise.reject(error);
}
);
export default apiClient;
Using Axios Interceptors in Components
Now, import and use apiClient
in your React components:
import React, { useEffect, useState } from 'react';
import apiClient from '../utils/apiClient';
const UserProfile = () => {
const [user, setUser] = useState(null);
useEffect(() => {
apiClient.get('/user/profile')
.then(response => setUser(response.data))
.catch(error => console.error('Error fetching profile:', error));
}, []);
return user ? <div>Welcome, {user.name}!</div> : <div>Loading...</div>;
};
export default UserProfile;
Implementing Middleware in React
Middleware in Redux acts as a bridge between the action and the reducer. It is useful for logging, authentication, API calls, caching, and performance optimization.
Why Use Middleware?
Centralized API request logic
Logging and debugging
Error handling and monitoring
Performance optimizations (throttling, caching, debouncing)
Request deduplication to prevent redundant API calls
Setting Up Middleware in Redux Toolkit
Installing Redux Toolkit
npm install @reduxjs/toolkit react-redux
Creating Custom Middleware for Logging and API Handling
// src/redux/middleware/loggerMiddleware.js
const loggerMiddleware = (store) => (next) => (action) => {
console.log('Dispatching:', action);
const result = next(action);
console.log('Next State:', store.getState());
return result;
};
export default loggerMiddleware;
Creating API Middleware for Handling Requests and Caching
// src/redux/middleware/apiMiddleware.js
const apiMiddleware = (store) => (next) => async (action) => {
if (action.type !== 'api/call') return next(action);
const { url, method, data, onSuccess, onError } = action.payload;
try {
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: data ? JSON.stringify(data) : undefined,
});
if (!response.ok) throw new Error('API call failed');
const responseData = await response.json();
store.dispatch({ type: onSuccess, payload: responseData });
} catch (error) {
store.dispatch({ type: onError, payload: error.message });
}
};
export default apiMiddleware;
Applying Middleware in the Redux Store
// src/redux/store.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './slices/userSlice';
import loggerMiddleware from './middleware/loggerMiddleware';
import apiMiddleware from './middleware/apiMiddleware';
const store = configureStore({
reducer: { user: userReducer },
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(loggerMiddleware, apiMiddleware),
});
export default store;
Conclusion
By implementing Axios interceptors and Redux middleware, we achieve: ✅ Performance Optimization - Reducing redundant API calls, caching responses, and centralizing logic. ✅ Code Reduction - Avoiding boilerplate authentication and error-handling code. ✅ Better Maintainability - Keeping API and state management logic clean and reusable. ✅ Improved Debugging - Enhanced logging and monitoring of actions.
Interceptors and middleware are essential tools in modern React development, allowing developers to enhance efficiency and optimize performance seamlessly. Start implementing them today and see the improvements in your project! 🚀
Subscribe to my newsletter
Read articles from Omkar Mante directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
