Axios with Redux Toolkit and createAsyncThunk
This article explores how the Axios HTTP client integrates with Redux
Toolkit's createAsyncThunk API to manage asynchronous data
fetching in modern React applications. It covers the core mechanics of
dispatching asynchronous actions, executing HTTP requests via Axios,
handling lifecycle states (pending, fulfilled,
rejected), and normalizing API errors for seamless global
state management.
The Roles of Axios and Redux Toolkit
Managing asynchronous API requests in a Redux application involves two primary layers: the transport layer and the state management layer.
- Axios (Transport Layer): Axios is a promise-based HTTP client responsible for sending network requests, setting headers, serializing request data, parsing JSON responses automatically, and intercepting request/response lifecycles.
- Redux Toolkit &
createAsyncThunk(State Layer):createAsyncThunkis an abstraction provided by Redux Toolkit (RTK) that accepts an action type string and a "payload creator" callback function. It automatically generates and dispatches Redux action types based on the returned promise's lifecycle:pending,fulfilled, orrejected.
When paired, Axios performs the network operation inside the
createAsyncThunk payload creator, while RTK automatically
propagates the resulting promise state to the Redux store via
extraReducers.
Step-by-Step Workflow
1. Defining the Async Thunk
Inside a Redux slice or separate API file,
createAsyncThunk wraps an Axios call. The payload creator
receives the argument passed to the thunk and a thunkAPI
object.
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
export const fetchUserData = createAsyncThunk(
'user/fetchUserData',
async (userId, { rejectWithValue }) => {
try {
const response = await axios.get(`/api/users/${userId}`);
// Axios stores the parsed JSON payload in response.data
return response.data;
} catch (error) {
// Axios error objects store server responses in error.response
if (!error.response) {
throw error;
}
return rejectWithValue(error.response.data);
}
}
);2. Handling
State in the Slice with extraReducers
The slice's extraReducers builder responds to the three
promise lifecycle states generated by fetchUserData.
import { createSlice } from '@reduxjs/toolkit';
import { fetchUserData } from './userThunks';
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUserData.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUserData.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUserData.rejected, (state, action) => {
state.loading = false;
// action.payload contains the rejectWithValue argument
state.error = action.payload || action.error.message;
});
},
});
export default userSlice.reducer;3. Dispatching from a Component
When a component calls dispatch(fetchUserData(userId)),
the workflow proceeds as follows:
fetchUserData.pendingis immediately dispatched, updatingloadingtotrue.- Axios executes the HTTP request.
- If the request succeeds (HTTP 2xx), the promise resolves, and
fetchUserData.fulfilledis dispatched withresponse.dataasaction.payload. - If the request fails (HTTP 4xx/5xx or network failure), the promise
rejects. Using
rejectWithValue,fetchUserData.rejectedis dispatched with the custom error payload.
Key Integration Considerations
- Error Serialization: Standard JavaScript
Errorobjects cannot be properly serialized into Redux actions. Axios errors contain non-serializable properties (like therequestconfiguration). Always extract only serializable data (such aserror.response.dataorerror.message) usingthunkAPI.rejectWithValue. - Axios Interceptors: Global Axios interceptors can
be configured for authentication tokens or refreshing tokens. These run
independently of Redux, ensuring
createAsyncThunkreceives ready-to-use responses without duplicating authentication logic inside every thunk. - Cancellation with
AbortController:createAsyncThunkexposes anabortsignal viathunkAPI.signal. This can be passed directly to Axios'ssignalconfiguration to cancel ongoing HTTP requests when a thunk is aborted or when a component unmounts.