React useReducer with Middleware - Robin Wieruch

React useReducer with Middleware - Robin Wieruch

In this React Hooks tutorial, I want to show you how to use a middleware for React’s useReducer Hook. This middleware would run either before or after the state transition of the reducer and enables you to opt-in features.

Before we can start, let’s establish what we have as a baseline from the previous useReducer tutorial: Our React application looks like the following.

First, we have all of our items — which serve as our initial state and which will become stateful eventually — in a list:

Second, we have our reducer function , which enables us to transition from one state to another state by using actions:

And last but not least, we have our React component which uses React’s useReducer Hook from the previous React Hooks tutorial:

From here, we want extend the application — to be more specific the reducer — with a middleware. The simplest middleware would be a logger which would output something before or after the reducer’s state transition. Let’s get started.

The logger middleware we want to establish for our reducer as an example could look like the following function which outputs the reducer’s action — which is in charge of transition our state from one state to another state — to the developer’s console log:

In our usage of React’s useReducer Hook, we would want to use the middleware the following way:

What we have right now could be pretty straightforward if React’s useReducer Hook would support middleware usage natively. But it doesn’t, so we need to come up with a custom hook :

With the middleware function at our hands in the custom hook, we can enhance the useReducer’s dispatch function with a higher-order function :

What we return from the custom hook is not the dispatch function anymore, but an extended version of it where we pass the action through the middleware before we pass it to the dispatch function.

You could check when this middleware executes, before or after the dispatch function which performs the state transition, if you would insert a logging statement in your reducer function:

That’s it for a very basic reducer middleware, however, we are lacking two crucial features: First, we are only able to use one middleware function in this custom hook. And second, the middleware always executes before the state transition with dispatch, so what if we would want to have it executing after the state transition instead. Let’s tackle these limitations next.

What we maybe want to have is multiple middleware functions that we can pass to the custom hook. In the following scenario, we pass two times the same middleware function as an array:

The custom hook changes the following way to execute multiple middleware functions:

Because we are able to pass multiple middleware functions to our custom useReducer hook, we solved the first limitation. However, all middleware functions still execute before the state transition with the actual dispatch function. Let’s tackle this last limitation.

Let’s say we have two middleware functions whereas one executes before and the other one executes after the state transition:

Event though the logging and the name of the functions are different, the functions are doing the same thing. So we need a way to tell them when (before or after dispatch) to execute. A straigthforward way would be using two arrays that we pass to our custom hook:

Then our custom reducer hook could act upon the middleware functions which run before as we had it before. In a naive approach, we would simply put the afterware functions after the dispatch function:

However, this doesn’t work, because dispatch updates the state asynchronously. So instead, we can wait for any state change in a useEffect hook :

For the afterward functions, we don’t have the action at our disposal anymore. We can change this by using a ref instance variable — which will be written before we dispatch the action and which can then be read after we dispatched the action:

In addition, this instance variable adds the benefit of not having the side-effect function in our useEffect hook execute on mount for the component. Instead it only executes once the action has been set.

We are done with our middleware and afterware. If you want to pass in more information to your middleware/afterware functions, you can do it like this:

That’s it. You are now able to run functions prior and after changing the state with React’s useReducer Hook by using middleware and afterware.

Recommended articles