Open In App

Why Do We Need Middleware for Async Flow in Redux?

Last Updated : 07 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In Redux, actions are dispatched synchronously, and the state is updated immediately by the reducers. However, real-world applications often involve asynchronous operations like API calls or time-consuming tasks. Without middleware, handling such operations in Redux can lead to complex nested callbacks or promises, making the code difficult to manage. Middleware provides a way to intercept and modify actions before they reach the reducers, enabling developers to handle asynchronous operations elegantly.

Prerequisites:

Need of Middleware in Redux

Handling Asynchronous Actions:

Middleware such as redux-thunk or redux-saga allows Redux to process asynchronous actions. redux-thunk lets action creators return functions instead of plain action objects, enabling asynchronous operations like API calls. redux-saga uses generator functions to manage complex async flows and side effects, offering a more powerful alternative for handling async logic.

Enhancing Action Creators:

Middleware enables action creators to perform asynchronous operations before dispatching actions. For example, with redux-thunk, you can write an action creator that dispatches multiple actions: one before starting an async operation, one during, and one after completion. This makes it easier to manage loading states and handle errors.

Centralizing Side Effects:

Middleware centralizes side effects management, improving code organization. Instead of spreading async logic across components, middleware provides a dedicated space to handle side effects. This separation of concerns results in cleaner and more maintainable code, as side effects are managed independently of state updates

Approaches

To use the middleware in Redux for handling async flow we can use the following opitons:

  • Redux Thunk: A middleware that allows you to write action creators that return a function instead of an action object.
  • Redux Saga: A middleware library which is used to allow a Redux store to interact asynchronously with resources outside of itself.
  • Redux Observable: A middleware that allows you to handle asynchronous operations using RxJS Observables.

Steps to Handle Asynchronous Operations using Redux Thunk

Step 1: Install required modules

First, you need to install the required modules for your Redux application with Redux Thunk:

npm install axios redux react-redux redux-thunk

Project Structure:

Screenshot-2024-06-18-155505[1]

Step 2: Check the Updated dependencies in package.json file:

After installing the required modules, you need to update the package.json file with the corresponding dependencies:

"dependencies": {
    "axios": "^1.7.2",
    "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"
  }

Step 3: Create a Redux store with the Redux Thunk middleware

JavaScript
// Filename - store.js

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";

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

Step 4: Create an asynchronous action creator using Redux Thunk. and define the reducers.

JavaScript
// Filename - src/actions.js

import axios from "axios";

export const fetchData = () => async (dispatch) => {
	try {
		const response = await axios.get(
			"https://jsonplaceholder.typicode.com/posts"
		); // Example endpoint
		dispatch({
        	type: "FETCH_DATA_SUCCESS",
            payload: response.data
        });
	} catch (error) {
		dispatch({
        	type: "FETCH_DATA_FAILURE",
            payload: error.message
		});
	}
};
JavaScript
// Filename: src/reducer.js

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

const dataReducer = (state = initialState, action) => {
    console.log(action);
    switch (action.type) {
        case "FETCH_DATA_REQUEST":
            return { ...state, loading: true };
        case "FETCH_DATA_SUCCESS":
            return { ...state, loading: false, items: action.payload };
        case "FETCH_DATA_FAILURE":
            return { ...state, loading: false, error: action.payload };
        default:
            return state;
    }
};

export default dataReducer;
JavaScript
import { combineReducers } from "redux";
import dataReducer from "../reducer";

const rootReducer = combineReducers({
    data: dataReducer
});

export default rootReducer;

Step 5: Dispatch the asynchronous action from your component and use the component in App.js file.

JavaScript
// Filename - components/MyComponent.js

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchData } from "../actions";

const MyComponent = () => {
	const dispatch = useDispatch();
	const { items, loading, error } = useSelector((state) => {
		console.log(state);
		return state.data;
	});

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

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

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

	return (
		<div>
			<h1>List of Items</h1>
			<p>Items in state: </p>
			<ul>
				{items.map((item) => (
					<li key={item.id}>
						<h3>{item.title}</h3>
						<p>{item.body}</p>
					</li>
				))}
			</ul>
		</div>
	);
};

export default MyComponent;
JavaScript
// Filename: src/App.js

import React from "react";
import { Provider } from "react-redux";
import store from "./store";
import MyComponent from "./components/MyComponent";

const App = () => (
	<Provider store={store}>
		<div className="App">
			<MyComponent />
		</div>
	</Provider>
);

export default App;

Output:

gfg-article
This is sample data fetched from 'https://jsonplaceholder.typicode.com/posts'

Conclusion

Middleware is essential for handling asynchronous operations in Redux, enabling smooth integration of side effects and complex async workflows. By leveraging middleware, such as redux-thunk or redux-saga, developers can maintain clean, scalable code and efficiently manage asynchronous logic in their Redux applications.


Similar Reads