Skip to main content

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, and rejected 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:

  1. Loading: The request is in progress.
  2. Succeeded: The request was successful and we have the data.
  3. 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 in createSlice that allows you to handle actions that were not defined in the reducers field.

Further Reading