Handling Asynchronous Actions with Redux Toolkit and Thunks
In modern web applications, managing asynchronous actions like API calls is a common challenge. Redux Toolkit simplifies this process using thunks. In this guide, we’ll cover how to handle async logic, like fetching data from an API, and integrate it into your Redux flow.
What are Thunks?
A thunk is a function that delays the execution of another function. In Redux, it’s a middleware that allows you to write async logic before dispatching actions to the store.
Redux Toolkit includes createAsyncThunk
, a utility that simplifies writing thunks for async operations.
Scenario: Fetching Data from an API
We’ll build an example to fetch a list of users from a public API and display it in a React app.
Step 1: Setting Up Redux with Async Thunks
Install Axios (or use fetch API):
npm install axios
Create a Slice with Async Logic
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// Async action
export const fetchUsers = createAsyncThunk('users/fetchUsers', async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
return response.data;
});
// Slice
const usersSlice = createSlice({
name: 'users',
initialState: {
users: [],
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.status = 'succeeded';
state.users = action.payload;
})
.addCase(fetchUsers.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
export default usersSlice.reducer;
Explanation:
-
createAsyncThunk
: Handles async logic (API call) and generates pending, fulfilled, and rejected actions. -
extraReducers
: Updates state based on the lifecycle of the async thunk.
Step 2: Configure the Store
import { configureStore } from '@reduxjs/toolkit';
import usersReducer from './usersSlice';
export const store = configureStore({
reducer: {
users: usersReducer,
},
});
Step 3: Provide the Store
Wrap your app with the Provider
component:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Step 4: Create a Component to Fetch and Display Data
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchUsers } from './usersSlice';
const UsersList = () => {
const dispatch = useDispatch();
const users = useSelector((state) => state.users.users);
const status = useSelector((state) => state.users.status);
const error = useSelector((state) => state.users.error);
useEffect(() => {
if (status === 'idle') {
dispatch(fetchUsers());
}
}, [status, dispatch]);
return (
<div>
<h1>Users List</h1>
{status === 'loading' && <p>Loading...</p>}
{status === 'failed' && <p>Error: {error}</p>}
{status === 'succeeded' && (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
);
};
export default UsersList;
Explanation:
-
useEffect
: Fetches users when the component mounts. -
useSelector
: Retrieves state (users
,status
, anderror
) from the Redux store. -
useDispatch
: Dispatches thefetchUsers
thunk to fetch data.
Step 5: Run the App
Start the development server:
npm start
You’ll see a loading indicator, followed by a list of users fetched from the API.
How It Works
-
Thunk Execution:
- The
fetchUsers
thunk sends an API request. - Redux Toolkit dispatches pending, fulfilled, or rejected actions based on the result.
- The
-
State Updates:
- The
extraReducers
in the slice updates the state according to the action.
- The
-
React Integration:
-
useSelector
subscribes components to Redux state. -
useDispatch
triggers the thunk to perform async operations.
-
Advantages of Redux Toolkit with Thunks
-
Simplified Async Handling:
- No need for custom middleware or boilerplate code.
- Automatically generates action types for async thunks.
-
Enhanced Readability:
-
createAsyncThunk
abstracts complex async logic. -
extraReducers
provide a clear mapping of actions to state updates.
-
-
Built-In DevTools Support:
- Redux DevTools can trace async actions seamlessly.
Conclusion
Redux Toolkit’s createAsyncThunk
makes handling async operations in Redux intuitive and efficient. In this example, we covered:
- Fetching data from an API.
- Managing the async state lifecycle (loading, success, error).
- Integrating async logic with React components.
With this foundation, you can confidently build apps requiring robust state management and async workflows. Stay tuned for more advanced Redux concepts, including middleware and real-world integration with MERN stack applications!
Top comments (0)