Open In App

Handling asynchronous actions with Redux Thunk

Last Updated : 10 Jun, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Managing asynchronous actions using Redux Thunk is a prevalent approach in Redux-based applications. Redux Thunk serves as middleware, enabling the creation of action creators that return functions rather than action objects. These functions can execute asynchronous tasks, like fetching data from an API, prior to dispatching actions to modify the Redux store.

We generally assume that Redux reducers must not contain "side effects". A "side effect" is any change to state or behaviour that can be seen outside of returning a value from a function.

Common examples of side effects are:

  • Logging data to the console
  • Writing to a file
  • Initiating an asynchronous timer
  • Sending AJAX HTTP requests

Despite these restrictions, practical applications inevitably require such functionalities at some point. In such case we can use middleware.

Steps for creating an application

Step 1: Create a new React project using Create React App or any other method you prefer.

npx create-react-app userfetchpro

Step 2: Install Redux ,React-Redux and redux-thunk packages using npm or yarn.

npm install redux react-redux redux-thunk
or
yarn add redux react-redux redux-thunk

Below is a concise project structure for a counter application developed using Redux:

Project Structure

folder
gfg

Updated dependencies in package.json file:

 "dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-redux": "^9.1.2",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"web-vitals": "^2.1.4"
},

Approach

  • Set up Redux store with Redux Thunk middleware:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
// Assuming you have a rootReducer
import rootReducer from './reducers';

const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
  • Write action creators that return functions:

const fetchData = () => {
return async (dispatch) => {
dispatch({ type: 'FETCH_DATA_REQUEST' });

try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_DATA_FAILURE', payload: error.message });
}
};
};
  • Dispatch the action creator from your components:
import { useDispatch } from 'react-redux';
import { fetchData } from './actions';

const MyComponent = () => {
const dispatch = useDispatch();

useEffect(() => {
dispatch(fetchData());
}, [dispatch]);

};
  • This setup allows you to handle asynchronous actions in Redux easily. The action creator fetchData returns a function that receives dispatch as an argument. Inside this function, you can perform asynchronous operations and dispatch regular synchronous actions to update the Redux store based on the result of the asynchronous operation.

Example: Below is an example of How to mock the Redux store for testing purposes.

JavaScript
import React from 'react';
import { Provider } from 'react-redux';
import store from './configureStore';
import UserList from './UserList';

const App = () => {
    return (
        <Provider store={store}>
            <div>
                <h1>User Fetch Pro</h1>
                <UserList />
            </div>
        </Provider>
    );
};

export default App;
JavaScript
import { createStore, applyMiddleware } from 'redux';
import {thunk} from 'redux-thunk';
import rootReducer from './userReducer';

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;
JavaScript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
JavaScript
async function getUsers() {
  return fetch("https://jsonplaceholder.typicode.com/users")
    .then((response) => response.json())
    .then((json) => {
      return json;
    })
    .catch((error) => {
      throw error;
    });
}

export default getUsers;
JavaScript
import getUsers from "./service";

export const FETCH_USERS_REQUEST = "FETCH_USERS_REQUEST";
export const FETCH_USERS_SUCCESS = "FETCH_USERS_SUCCESS";
export const FETCH_USERS_FAILURE = "FETCH_USERS_FAILURE";

export const fetchUsers = () => {
  return async (dispatch) => {
    dispatch({ type: FETCH_USERS_REQUEST });
    try {
      getUsers().then((res) => {
        dispatch({ type: FETCH_USERS_SUCCESS, payload: res });
      });
    } catch (error) {
      dispatch({ type: FETCH_USERS_FAILURE, payload: error.message });
    }
  };
};
JavaScript
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from './userActions';

const UserList = () => {
    const dispatch = useDispatch();
    const { users, loading, error } = useSelector(state => state);
    useEffect(() => {
        dispatch(fetchUsers());
    }, [dispatch]);

    if (loading) {
        return <div>Loading...</div>;
    }

    if (error) {
        return <div>Error: {error}</div>;
    }

    return (
        <div>
            <h2>User List</h2>
            <ul>
                {users && users.length>0 && users.map(user => (
                    <li key={user.id}>{user.name}</li>
                ))}
            </ul>
        </div>
    );
};

export default UserList;
JavaScript
import { FETCH_USERS_REQUEST, FETCH_USERS_SUCCESS, FETCH_USERS_FAILURE } from './userActions';

const initialState = {
    users: [],
    loading: false,
    error: null
};

const userReducer = (state = initialState, action) => {
    switch (action.type) {
        case FETCH_USERS_REQUEST:
            return { ...state, loading: true, error: null };
        case FETCH_USERS_SUCCESS:
            return { ...state, loading: false, users: action.payload, error: null };
        case FETCH_USERS_FAILURE:
            return { ...state, loading: false, error: action.payload };
        default:
            return state;
    }
};

export default userReducer;

Command to Run the project:

npm start
or
yarn start

Output:

output
gfg

Next Article

Similar Reads