DEV Community

Cover image for Redux Middleware: How to use and create Middleware in Redux
CaptainPrinz
CaptainPrinz

Posted on

Redux Middleware: How to use and create Middleware in Redux

Introduction

With Redux reducers being pure functions, It is impossible to run any side effect in the reducer function. Thus, tasks like fetching data from an API just wouldn't work in reducer functions. This presents a problem for developers unfamiliar with Redux. Luckily, Redux has an ace up its sleeve for running side effects and it's called a middleware.

What is a Middleware

Broadly speaking, middleware can be considered as a layer that connects two different entities. A Middelwares act as a translation layer to enable two different entities to communicate effectively, this is done by intercepting the incoming data, processing the data and sending it to the target entity. In Redux, the entities are actions and Reducers while the middleware acts as the translation layer.

However, middleware does not translate anything but rather allows us to run side effects between the time an action is dispatched and before it gets to the reducer.

The logic behind middleware is that since a Reducer function can not run side effects, middleware could act as a portal to an alternate universe where side effects can be performed based on the action that was dispatched before the actions reach a reducer.

Notable uses cases for middleware

Redux middlewares have several use cases but the following are the most notable:

  • Data Fetching - In JAMSTACK applications, data is usually fetched on page load, such a task is performed in Redux with the help of middleware.

  • Logging - This is applicable in scenarios where there's a need to capture the state before an action is dispatched.

Other notable uses cases include

  • Crash reporting.

  • Modifying actions.

Anatomy of a Middleware

To create a middleware in Redux, we simply define a function that returns 2 nested inner functions. The implementation can be found below

const myMiddleware = function(storeAPI) {
  return function(next) {
    return function(action){
      //do something
      return next(dispatch)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

It is important to know what each parameter represents

  1. The storeAPI parameter is an object with access to the dispatch() and getState() methods.

  2. The next parameter is a function for passing the action to the next middleware in the middleware queue(to be discussed).

  3. The action parameter represents the current action that was dispatched.

Integrating a middleware with the Store

Now that we have a middleware, it's time to integrate it with the store. Luckily, we can achieve that with the applyMiddleware function. The function takes in a middleware as an argument.

import { createStore, applyMiddleware } from "redux"

const store = createStore(reducer, applyMiddleware(middleware))
Enter fullscreen mode Exit fullscreen mode

The applyMiddleware function can take as many arguments as possible

The Middleware Queue

When an action is dispatched, Redux sets up a queue of middleware for passing action much like how actions are passed between Reducers.

But Redux applications can have more than one middleware, thereby passing actions between middleware until they finally reach the reducer.

However, it is important to note that If the dispatch() method is called in a middleware, Redux restarts the pipeline process by passing the action from the first middleware in the queue until it reaches the last middleware.

Creating a Middleware

Having discussed the anatomy of middleware and the middle pipeline, we'll put our knowledge into practice by creating two middlewares for logging and fetching data from external servers respectively.

Although middlewares for the above purposes already exist, recreating one from scratch will you an idea of how the logic is structured.

Data Logging middleware

We'll create a basic middle for logging the state value before an action is dispatched and the current action as well.

The code below shows the implementation.

const logger = function(storeAPI){
  return function(next){
    return function(action){
      console.log("Current State\n", storeAPI.getState())
      console.log("Dispaching Action\n", action)
      return next(action)
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The snippet below shows the Code in action.

%[https://codepen.io/Captain-Prinz/pen/ExdQjxN]

We can finally see the action being dispatched as well as the app state before the action was dispatched.

Data Fetching Middleware

We take things a little further here by creating a middleware for fetching data from external APIs.

To keep things simple, We're going to create a middleware that performs the following

  1. Check if an action is a function.

  2. Execute the API request function and dispatch another action with the API result as payload.

The function to be passed in the dispatch method will take the dispatch argument. That way, we can dispatch another action within the function.

The code below shows the structure of what we are trying to achieve.

const reducer = (state={data: {}}, action) => {
  if(action.type === "ADD_DATA"){
    return {data: action.payload}
  }
  return state
}

const getAPIData = async function(dispatch){
  try{
    const { data } = await axios.get("https://www.random-data-api.com/api/users/random_user/")
    dispatch({type: "ADD_DATA", payload: data});
    console.log(data)
}catch(e){
    console.log("error\n",e)
  }
}

const fetcher = function({dispatch}){
  return function(next){
    return function(action){
      if(typeof action === "function"){
        return action(dispatch) 
      }
      return next(action)
    }
  }
}

const store = createStore(userReducer, applyMiddleware(fetcher))
Enter fullscreen mode Exit fullscreen mode

I would've shown a Live snippet but for some reason, Codepen seems to be messing up with the Axios request.

Anyways, We dispatch the function like a regular function and when that happens, the API result is taken to the reducer which saves it in the store.

Conclusion

We've finally seen how middlewares can be used to run side effects, especially Data Fetching. With this, we wrap up the article on Redux and focus on integration with popular UI libraries.

If there's any technology you want me to cover, please consider leaving a comment and sharing with others if you find the article useful.

Thank you

Top comments (0)