Async Logic with `createAsyncThunk` (Part 2): A Practical Example #117
📖 Introduction
Following our introduction to createAsyncThunk
, this article provides a practical, hands-on example of how to use it in a React application. We will build a simple application that fetches a list of posts from an API and displays them.
📚 Prerequisites
Before we begin, please ensure you have a solid grasp of the following concepts:
- All concepts from Part 1 of this series.
- How to set up a Redux store and connect it to a React application.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ Creating an Async Thunk: How to create an async thunk to fetch a list of posts.
- ✅ Handling Lifecycle Actions: How to handle the
pending
,fulfilled
, andrejected
actions in a slice. - ✅ Practical Application: Building a simple application that fetches and displays a list of posts.
🧠 Section 1: The Core Concepts of Fetching Data with createAsyncThunk
When fetching data from an API, we need to handle three different states:
- Loading: The request is in progress.
- Succeeded: The request was successful and we have the data.
- Failed: The request failed and we have an error.
createAsyncThunk
helps us to manage these states by automatically dispatching pending
, fulfilled
, and rejected
actions. We can then handle these actions in our slice to update the state accordingly.
💻 Section 2: Deep Dive - Implementation and Walkthrough
Let's build our application.
2.1 - The postsSlice.js
File
First, let's create a postsSlice.js
file in our features/posts
directory.
// features/posts/postsSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
return data;
});
const postsSlice = createSlice({
name: 'posts',
initialState: {
posts: [],
status: 'idle',
error: null,
},
reducers: {},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state, action) => {
state.status = 'loading';
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded';
state.posts = state.posts.concat(action.payload);
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
export default postsSlice.reducer;
2.2 - The store.js
File
Now, let's add our postsReducer
to our store.
// app/store.js
import { configureStore } from '@reduxjs/toolkit';
import postsReducer from '../features/posts/postsSlice';
export default configureStore({
reducer: {
posts: postsReducer,
},
});
2.3 - The PostsList
Component
Now, let's create a component to display our posts.
// components/PostsList.js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchPosts } from '../features/posts/postsSlice';
function PostsList() {
const dispatch = useDispatch();
const posts = useSelector(state => state.posts.posts);
const postStatus = useSelector(state => state.posts.status);
const error = useSelector(state => state.posts.error);
useEffect(() => {
if (postStatus === 'idle') {
dispatch(fetchPosts());
}
}, [postStatus, dispatch]);
let content;
if (postStatus === 'loading') {
content = <div>Loading...</div>;
} else if (postStatus === 'succeeded') {
content = posts.map(post => <div key={post.id}>{post.title}</div>);
} else if (postStatus === 'failed') {
content = <div>{error}</div>;
}
return (
<section>
<h2>Posts</h2>
{content}
</section>
);
}
export default PostsList;
💡 Conclusion & Key Takeaways
In this article, we've built a practical example of how to use createAsyncThunk
to fetch data from an API. We've seen how to create an async thunk, handle the different stages of the request in our slice, and display the data in a React component.
Let's summarize the key takeaways:
createAsyncThunk
is a powerful tool for handling asynchronous logic in Redux Toolkit.- It simplifies the process of managing loading and error states.
- By using
createAsyncThunk
, you can write cleaner and more maintainable asynchronous code.
Challenge Yourself:
To solidify your understanding, try to add a "Refresh" button to the PostsList
component that re-fetches the posts from the API.
➡️ Next Steps
You now have a solid understanding of how to use createAsyncThunk
. In the next article, "Structuring a Modern Redux App", we will discuss best practices for organizing your Redux files and folders.
Thank you for your dedication. Stay curious, and happy coding!
glossary
extraReducers
: A field increateSlice
that allows you to handle actions that were not defined in thereducers
field.