Open In App

Writing Custom middlewares in React Redux

Last Updated : 02 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Custom middleware in React Redux is like having a helper that can do things with actions before they’re handled by our app. It allows us to intercept actions and apply custom logic before they reach the reducers. In this article, we are going to discuss how it is done.

What is Redux middleware?

Redux middleware is a mechanism that sits between dispatching an action and the moment it reaches the reducer. It allows you to intercept actions and execute custom logic, such as logging, asynchronous operations, or modifying actions before they reach the reducers.

What is Custom Middleware?

Custom middleware in Redux refers to user-defined functions that intercept actions dispatched to the store before they reach the reducers. These functions enable developers to add custom logic such as logging, modifying actions, or performing asynchronous operations.

When to use Custom Middleware?

Use custom middleware in Redux when you need to:

  • Handle asynchronous operations such as API requests or timers.
  • Perform logging, authentication, error handling, or data transformation.
  • Monitor performance metrics for optimization purposes.
  • Cache data locally to reduce API calls and improve performance.

Standard patterns to follow for creating custom middleware

When creating custom middleware for Redux, there are several standard patterns you can follow. These patterns allow you to create side effects for actions, modify or cancel actions, or modify the input accepted by dispatch. Here are some standard patterns for middleware:

  • Creating Side Effects: Middleware can create side effects for actions by intercepting them before they reach reducers, allowing for tasks like logging, dispatching additional actions, or handling asynchronous operations.
  • Modifying or Canceling Actions: Middleware can modify actions before they reach reducers, such as adding metadata or modifying payload data. It can also cancel actions altogether based on certain conditions.
  • Modifying Input Accepted by Dispatch: Middleware can modify the input accepted by dispatch, enabling tasks like throttling or debouncing actions, or transforming data before it’s dispatched.

Rules to make compatible middleware

To ensure compatibility and adherence to Redux middleware conventions, custom middleware should follow these rules:

  • Calling the Next Middleware: Middleware functions should always call the next function to pass control to the next middleware in the chain. This ensures that all middleware in the stack have an opportunity to process the action.
JavaScript
const myMiddleware = store => next => action => {
    // Perform some logic before passing the action
    // to the next middleware
    console.log('Middleware processing:', action);

    // Call the next middleware in the chain
    return next(action);
};
  • Returning the Dispatch Return Value: Middleware should return the result of calling next(action), which is typically the return value of the dispatch function. This allows middleware further down the chain to access the return value of dispatch, which can be useful for composing middleware.
JavaScript
const myMiddleware = store => next => action => {
  // Perform some logic before passing 
  // the action to the next middleware
  console.log('Middleware processing:', action);
  
  // Call the next middleware in the 
  // chain and return its return value
  return next(action);
};

Steps to Setup React App and Installing Module:

Step 1: Create a new React application using Create React App:

npx create-react-app custom-middleware

Step 2: Navigate to the project directory:

cd custom-middleware

Project Structure:

Screenshot-2024-03-20-211859

Install Redux and React Redux:

npm install redux react-redux

The Updated dependencies in package.json file will look like.

 "dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^9.1.0",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"web-vitals": "^2.1.4"
},

Step 3: Creating required folder and files

  • Create redux folder inside src with customMiddleware.js ,store.js and reducers.js files
  • replace code of App.js and index.js with the code given below.
JavaScript
// src/redux/reducers.js

const initialState = {
    count: 0
};

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return {
                ...state,
                count: state.count + 1
            };
        default:
            return state;
    }
};

export default reducer;
JavaScript
// src/redux/customMiddleware.js

const customMiddleware = store => next => action => {
    console.log('Custom Middleware - Dispatching action:', action);


    // Pass the action to the next middleware or to the reducer
    const result = next(action);

    // You can also perform logic after the action has been dispatched

    return result;
};

export default customMiddleware;
JavaScript
// src/redux/store.js

import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import customMiddleware from './customMiddleware';

// Apply the custom middleware
const middlewareEnhancer = applyMiddleware(customMiddleware);

// Create the Redux store
const store = createStore(rootReducer, middlewareEnhancer);

export default store;
JavaScript
// src/index.js

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

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);
JavaScript
// src/App.js

import React from 'react';
import { connect } from 'react-redux';

const App = ({ count, increment }) => {
    return (
        <div>
            <h1>Count: {count}</h1>
            <button
                onClick={increment}>
                Increment
            </button>
        </div>
    );
};

const mapStateToProps = state => ({
    count: state.count
});

const mapDispatchToProps = dispatch => ({
    increment: () => dispatch({ type: 'INCREMENT' })
});

export default connect(mapStateToProps, mapDispatchToProps)(App);

Output:

mm



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads