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:
createSliceandconfigureStore.- 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
createAsyncThunkIs: How it works and what it does under the hood. - ✅ Core Implementation: How to use
createAsyncThunkto 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 callcreateAsyncThunkwith two arguments:- The action type string:
'users/fetchUsers'. - The payload creator function: an
asyncfunction that fetches the users and returns the data.
- The action type string:
fetchUsers: ThefetchUsersvariable 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 theextraReducersfield to handle actions that were not defined in thereducersfield.builder.addCase: We use thebuilder.addCasemethod to handle each of the lifecycle actions.fetchUsers.pending: When the request is pending, we set theloadingstate to'loading'.fetchUsers.fulfilled: When the request is successful, we set theloadingstate to'succeeded'and update theentitiesarray with the fetched data.fetchUsers.rejected: When the request fails, we set theloadingstate 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:
createAsyncThunkis the standard way to handle asynchronous logic in Redux Toolkit.- It automatically dispatches
pending,fulfilled, andrejectedactions based on the result of a promise. - You can handle these actions in your slice using the
extraReducersfield.
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
fulfilledaction.