Asynchronous Actions with createAsyncThunk in React JS

Redux Toolkit provides a powerful tool to handle asynchronous actions with minimal boilerplate through the createAsyncThunk function. This function allows you to dispatch asynchronous operations (such as API calls) and manage the lifecycle of those operations (pending, fulfilled, and rejected) in a Redux store.

In this article, we will explore how to use createAsyncThunk to handle asynchronous actions in a React app, manage loading, success, and error states, and update the Redux store accordingly.

1. What is createAsyncThunk?

createAsyncThunk is a utility function provided by Redux Toolkit to handle asynchronous logic in Redux. It automatically dispatches three actions for each async operation:

  • pending: Dispatched when the async action starts.
  • fulfilled: Dispatched when the async action succeeds.
  • rejected: Dispatched when the async action fails.

createAsyncThunk simplifies asynchronous flow in Redux by providing a standardized approach for dealing with async logic, including side effects like fetching data from an API.

2. Installing Redux Toolkit and React-Redux

To get started with Redux Toolkit and createAsyncThunk, you need to install Redux Toolkit and React-Redux in your project.

Example 1: Installing Redux Toolkit and React-Redux

        
          npm install @reduxjs/toolkit react-redux
        
      

Once installed, you can begin setting up your Redux store and actions.

3. Creating an Asynchronous Action with createAsyncThunk

Let's say you want to fetch a list of posts from an API. You can use createAsyncThunk to handle this asynchronous action. Redux Toolkit automatically handles the dispatching of the actions (pending, fulfilled, rejected) based on the result of the async operation.

Example 2: Fetching Posts with createAsyncThunk

        
          import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

          // Define an async thunk to fetch posts
          export const fetchPosts = createAsyncThunk(
            'posts/fetchPosts', // Action type
            async () => {
              const response = await fetch('https://jsonplaceholder.typicode.com/posts');
              return response.json(); // The returned value will be the payload
            }
          );

          // Create the slice with extraReducers to handle async actions
          const postsSlice = createSlice({
            name: 'posts',
            initialState: {
              posts: [],
              status: 'idle', // Can be 'idle', 'loading', 'succeeded', 'failed'
              error: null
            },
            reducers: {},
            extraReducers: (builder) => {
              builder
                .addCase(fetchPosts.pending, (state) => {
                  state.status = 'loading';
                })
                .addCase(fetchPosts.fulfilled, (state, action) => {
                  state.status = 'succeeded';
                  state.posts = action.payload;
                })
                .addCase(fetchPosts.rejected, (state, action) => {
                  state.status = 'failed';
                  state.error = action.error.message;
                });
            }
          });

          export default postsSlice.reducer;
        
      

In this example, we define an async thunk fetchPosts using createAsyncThunk. This function:

  • Triggers the pending state when the request starts.
  • Triggers the fulfilled state when the data is successfully fetched and updates the Redux state with the data.
  • Triggers the rejected state if the fetch fails and stores the error message in the Redux state.

4. Configuring the Redux Store

After creating the slice with the asynchronous actions, the next step is to configure the Redux store to use the created reducer.

Example 3: Setting Up the Redux Store

        
          import { configureStore } from '@reduxjs/toolkit';
          import postsReducer from './postsSlice';

          // Set up the Redux store
          const store = configureStore({
            reducer: {
              posts: postsReducer
            }
          });

          export default store;
        
      

In this example, we configure the store and include the postsReducer created from the slice. This will handle the state updates when the async actions are triggered.

5. Connecting the Redux Store to React

Next, you need to connect the Redux store to your React application using the Provider component from react-redux. This will allow the components to access the Redux store.

Example 4: Connecting Redux Store with the Provider

        
          import React from 'react';
          import ReactDOM from 'react-dom';
          import { Provider } from 'react-redux';
          import App from './App';
          import store from './store';

          ReactDOM.render(
            
              
            ,
            document.getElementById('root')
          );
        
      

Here, we wrap the App component with the Provider component and pass the Redux store to it. This makes the store accessible to all child components.

6. Using the Asynchronous Action in React Components

Finally, you can use the fetchPosts action inside your React components by dispatching it and accessing the state using the useDispatch and useSelector hooks.

Example 5: Displaying Posts in a React Component

        
          import React, { useEffect } from 'react';
          import { useDispatch, useSelector } from 'react-redux';
          import { fetchPosts } from './postsSlice';

          const Posts = () => {
            const dispatch = useDispatch();
            const posts = useSelector((state) => state.posts.posts);
            const status = useSelector((state) => state.posts.status);
            const error = useSelector((state) => state.posts.error);

            useEffect(() => {
              if (status === 'idle') {
                dispatch(fetchPosts());
              }
            }, [dispatch, status]);

            return (
              

Posts

{status === 'loading' &&

Loading...

} {status === 'failed' &&

{error}

}
    {posts.map(post => (
  • {post.title}
  • ))}
); }; export default Posts;

In this component, we use:

  • useDispatch to dispatch the fetchPosts async action when the component is mounted.
  • useSelector to access the status, posts, and error from the Redux store.
  • Conditional rendering to show a loading message, the posts, or an error message based on the current status of the async action.

7. Conclusion

createAsyncThunk is a powerful utility in Redux Toolkit that simplifies handling asynchronous actions like API calls in React applications. By dispatching pending, fulfilled, and rejected actions automatically, Redux Toolkit reduces the boilerplate code and provides a streamlined approach to managing async workflows in your app.

With createAsyncThunk, you can manage the loading, success, and error states for async operations, keeping your Redux store in sync with the lifecycle of asynchronous actions. This results in a cleaner and more efficient Redux setup.





Advertisement