React 18 Redux Handle API Calls with Thunk Middleware Tutorial

Last Updated on by in React JS

In this React tutorial, we will learn how to work with asynchronous HTTP requests in the React js application using the Redux store and the Redux toolkit libraries.

We will specifically learn the recommended way of using builder callback notation in extraReducers for handling HTTP requests with pending, fulfilled, and rejected states.

This tutorial will step-by-step cover how to set up a basic React app, how to set up Redux Store in React app from scratch and how to create Reducers and Action creator functions using the createSlice, and createAsyncThunk functions.

The createSlice is a function available via the redux toolkit library.

It accepts an initial state, an object of reducer functions, and a “slice name”, and by default, creates action creators and action types that coordinate with the reducers and state.

The createAsyncThunk is used to handle asynchronous requests.

It produces a definitive Redux thunk action creator. The thunk action creator returns action creators that offer pending, fulfilled, and rejected cases hooked as nested fields.

Why should we rely on async thunk?

Redux actions creators are synchronous by default. Redux thunk middleware mainly used to handle async operations.

It allows you to create action creator functions that helps in handling the requests which returns the output after certain delay.

How to Handle Asynchronous HTTP Requests in React with React Redux and Thunk Middleware

  • Step 1: Install React App
  • Step 2: Install Essential Libraries
  • Step 3: Install Redux Store and Toolkit
  • Step 4: Create Redux Store
  • Step 5: Add Redux Provider in React
  • Step 6: Handle Asynchronous API Requests with Redux Toolkit
  • Step 7: Consume Store Data in Component
  • Step 8: Update App Js File
  • Step 9: Start Development Server

Install React App

To grasp the React HTTP request handling via a redux-thunk, we must have a React app set up in our development system.

npx create-react-app react-redux-thunk-example

Here is the project’s folder structure for React redux thunk example.

React redux thunk example

Install Essential Libraries

We need to install the bootstrap and Axios libraries in our project. Bootstrap will help us create the UI, whereas Axios will allow us to make the HTTP requests.

npm install axios bootstrap --legacy-peer-deps

Install Redux Store and Toolkit

In order to set up a redux store that handles asynchronous HTTP requests, we need to install react-redux, and redux toolkit libraries.

npm install react-redux @reduxjs/toolkit --legacy-peer-deps

Create Redux Store

Let us create the app/ folder in src/ folder there after create the store.js file.

The store is an object that holds the application state, the store is invoked through configureStore method and it is available through @reduxjs/toolkit library.

To form the store you need to define the reducer object, pass all the reducers using createSlice module in the reducer object.

import { configureStore } from '@reduxjs/toolkit'
import usersReducer from '../features/users/usersSlice'

export default configureStore({
  reducer: {
    users: usersReducer,
  },
})

Add Redux Provider in React

Head over to the index.js file, import Provider and store and define it around the App component.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import store from './app/store'
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render( 
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
)

Handle Asynchronous API Requests with Redux Toolkit

In this step, you require to create the features/ folder in the src/ directory then create the users/ folder then create the usersSlice.js file in the users/ directory.

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

export const getUsers = createAsyncThunk('users/getUsers', async () => {
  const response = await axios.get('https://jsonplaceholder.typicode.com/users')
  return response.data
})

export const usersSlice = createSlice({
  name: 'users',
  initialState: {
    data: [],
    loading: 'idle',
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(getUsers.pending, (state, action) => {
      if (state.loading === 'idle') {
        state.loading = 'pending'
      }
    })

    builder.addCase(getUsers.fulfilled, (state, action) => {
      if (state.loading === 'pending') {
        state.data = action.payload
        state.loading = 'idle'
      }
    })

    builder.addCase(getUsers.rejected, (state, action) => {
      if (state.loading === 'pending') {
        state.loading = 'idle'
        state.error = 'Error occured'
      }
    })
  },
})

export default usersSlice.reducer

We named our slice users, set initial state with data, loading and error values.

Define the createAsyncThunk function. The first argument contains the slice name and the action creator function name. In our case, it becomes ‘users/getUsers’ like this.

The second argument is the async function which helps makes the async request, or you may also define the logic which works on certain conditions.

The createAsyncThunk returns three states that are pending, fulfilled and rejected we are using it with builder callback notation.

Consume Store Data in Component

Next, you have to create the features/users/Users.js file. In this file, we are going to retrieve the store data and display via the component using useDispatch, useSelector and getUsers slice.

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getUsers } from './usersSlice'

const Cards = ({ user }) => {
  return (
    <div
      className="col-lg-4 mb-3 d-flex align-items-stretch h-100"
      key={user.id}
    >
      <div className="card">
        <div className="card-body">
          <h5 className="card-title">{user.name}</h5>
          <p className="card-text">{user.email}</p>
        </div>
      </div>
    </div>
  )
}

function Users() {
  const dispatch = useDispatch()
  const { data, loading, error } = useSelector((state) => state.users)

  useEffect(() => {
    dispatch(getUsers())
  }, [dispatch])

  let content

  if (loading === 'pending') {
    content = (
      <div className="d-flex justify-content-center">
        <div className="spinner-border" role="status">
          <span className="visually-hidden">Loading...</span>
        </div>
      </div>
    )
  }
  if (loading === 'idle') {
    content = data.map((item) => {
      return <Cards user={item} key={item.id} />
    })
  }
  if (error !== null) {
    content = (
      <div className="alert alert-danger" role="alert">
        {error}
      </div>
    )
  }

  return <div className="row">{content}</div>
}

export default Users

The getUsers returns the data, loading, and error states; we retrieved the states from the usersSlice using the JavaScript object destructuring method.

In the Users component, we are showing the loading state when loading is in a pending state; when the loading state is idle, we are showing the data using the map object and showing an error when the error occurred while fetching data.

Update App Js File

Go to App.js file, in this file you need to import and define the Users component.

import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
import './App.css'
import Users from './features/users/Users'

function App() {
  return (
    <div className="container">
      <div className="d-flex border-bottom pt-2 pb-2 mb-5">
        <div className="p-2 flex-grow-1">Redux Store</div>
        <div>
          <div>
            <a className="nav-link" aria-current="page" href="#">
              Users
            </a>
          </div>
        </div>
      </div>
      <Users />
    </div>
  )
}

export default App

Start Development Server

Lastly, we are going to start the react development server through provided command.

npm start

You can test the app on the following url.

http://localhost:3000

React Redux Handle API Calls with Thunk Middleware Tutorial

Conclusion

Redux is an open-source JavaScript library used to manage the application state.

It allows React components to read data from a Redux Store. React components dispatch actions and update the app state through the Store.

Redux store gives utmost flexibility to React components. React components read data from a centralized Redux store.

Redux store pattern is such where React components dispatch actions through action creator object and action creator function (in case of asynchronous requests) to update the UI through Redux store.

In this tutorial, we have learned how to handle asynchronous HTTP requests through Redux toolkit library and update the React UI via a Redux store.

You can download the complete code of this guide from here.