Async Logic with `createAsyncThunk` (Part 1): Handling API Calls the Redux Way #116
📖 Introduction
Following our exploration of configureStore
and createSlice
, this article dives into one of the most powerful features of Redux Toolkit: createAsyncThunk
. We will learn how to use this function to handle asynchronous logic, such as fetching data from an API, in a clean and standardized way.
📚 Prerequisites
Before we begin, please ensure you have a solid grasp of the following concepts:
createSlice
andconfigureStore
.- Asynchronous JavaScript (async/await).
- The Fetch API.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ The "Why" of
createAsyncThunk
: Understanding the need for a standardized way to handle asynchronous logic in Redux. - ✅ What
createAsyncThunk
Is: How it works and what it does under the hood. - ✅ Core Implementation: How to use
createAsyncThunk
to fetch data from an API.
🧠 Section 1: The Core Concepts of createAsyncThunk
In a Redux application, you'll often need to perform asynchronous operations, such as fetching data from an API. Before Redux Toolkit, this required using middleware like redux-thunk
and writing a lot of boilerplate code to dispatch actions for the different stages of the request (e.g., "pending", "fulfilled", "rejected").
createAsyncThunk
is a function that simplifies this process. It accepts an action type string and a "payload creator" callback function that should return a promise. It then automatically dispatches promise lifecycle actions (pending
, fulfilled
, rejected
) based on the result of the promise.
This allows you to write your asynchronous logic in a single place and have the different stages of the request handled automatically by your reducers.
💻 Section 2: Deep Dive - Implementation and Walkthrough
Let's see how to use createAsyncThunk
to fetch a list of users from an API.
2.1 - Creating the Async Thunk
First, let's create an async thunk in our usersSlice.js
file.
// features/users/usersSlice.js
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
export const fetchUsers = createAsyncThunk(
'users/fetchUsers',
async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
return data;
}
);
// ...
Step-by-Step Code Breakdown:
createAsyncThunk
: We callcreateAsyncThunk
with two arguments:- The action type string:
'users/fetchUsers'
. - The payload creator function: an
async
function that fetches the users and returns the data.
- The action type string:
fetchUsers
: ThefetchUsers
variable is now an async thunk action creator that we can dispatch from our components.
2.2 - Handling the Lifecycle Actions in the Slice
Now, we need to handle the pending
, fulfilled
, and rejected
actions in our slice. We do this using the extraReducers
field in createSlice
.
// features/users/usersSlice.js
// ...
export const usersSlice = createSlice({
name: 'users',
initialState: {
entities: [],
loading: 'idle',
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state, action) => {
state.loading = 'loading';
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.loading = 'succeeded';
state.entities = action.payload;
})
.addCase(fetchUsers.rejected, (state, action) => {
state.loading = 'failed';
});
},
});
export default usersSlice.reducer;
Step-by-Step Code Breakdown:
extraReducers
: We use theextraReducers
field to handle actions that were not defined in thereducers
field.builder.addCase
: We use thebuilder.addCase
method to handle each of the lifecycle actions.fetchUsers.pending
: When the request is pending, we set theloading
state to'loading'
.fetchUsers.fulfilled
: When the request is successful, we set theloading
state to'succeeded'
and update theentities
array with the fetched data.fetchUsers.rejected
: When the request fails, we set theloading
state to'failed'
.
💡 Conclusion & Key Takeaways
In this article, we've learned about createAsyncThunk
and how it simplifies asynchronous logic in Redux Toolkit. We've seen how to create an async thunk to fetch data from an API and how to handle the different stages of the request in our slice.
Let's summarize the key takeaways:
createAsyncThunk
is the standard way to handle asynchronous logic in Redux Toolkit.- It automatically dispatches
pending
,fulfilled
, andrejected
actions based on the result of a promise. - You can handle these actions in your slice using the
extraReducers
field.
Challenge Yourself:
To solidify your understanding, try to add error handling to the fetchUsers
thunk and display an error message in your component if the request fails.
➡️ Next Steps
You now have a basic understanding of createAsyncThunk
. In the next article, "Async Logic with createAsyncThunk
(Part 2)", we will build a more practical example of how to use this function in a React application.
Thank you for your dedication. Stay curious, and happy coding!
glossary
createAsyncThunk
: A function from Redux Toolkit that simplifies asynchronous logic.- Thunk: A function that is returned from another function. In Redux, a thunk is a function that can have side effects.
- Payload Creator: A function that returns a promise and is used to create the payload for the
fulfilled
action.