Learn How Redux Middleware Helps Solve Logging & Dispatch Problems



In the article, learn how Redux is used to maintain state of an application, we discussed actions and reducers in detail. In that tutorial, we explained how actions are used to describe changes that happen in an application. We also discussed how to use pure functions to write reducers that manage state. We will build on those concepts in this tutorial to discuss the problems solved by Redux middleware. Please refer to the tutorial for a review of actions and reducers.

Redux middleware offers the developer an opportunity to perform actions between the time an action is dispatched and the time it arrives at the reducer. Redux middleware is useful for logging, crash reporting, calling asynchronous APIs, and routing, among other uses. We will discuss the identified use cases in this tutorial.

Using Redux middleware for logging
To a developer, Redux is attractive because of predictability and transparency of state changes. With each action a new state is computed and saved. Therefore it is important as developers that we are able to log each action and the state created after it. If anything goes wrong, we are able to refer to the log and identify the action that corrupted our state.

There are three ways we can approach the logging problem. These are manual logging, dispatch wrapping and monkeypatching dispatch. We will discuss each of these approaches and demonstrate their use.

Manual logging
Manual logging is the simplest solution. You just need to log the action and state whenever you perform an action call. This is not really a practical solution but a way to help understand the problem of logging. When using react-redux bindings access to the store from components is not possible so consider manual logging a learning tool.

Consider an action call as shown below

To implement manual logging, we would rewrite our action call as shown below.

The above implementation correctly logs our action and state but implementing it in every action call is too laborious.

Wrapping dispatch
An alternative to manual logging is creating a function that extracts log information. We then pass our action calls to this function. An example of implementing such a function is shown below.

To extract logging information from our action calls we make calls as shown below

Although the above approach is better than manual logging, special function import is not very convenient when calling actions.

The two options we tried to implement earlier falls short of our requirements. This is where Redux middleware comes in. In the learn redux framework and the problems it solves tutorial, we explained how to set up Node and the npm package manager. Please refer to that tutorial to set up a development environment. To install redux middleware using npm, run the command below at a terminal

Middleware enables you to wrap the store dispatch method to implement your desired functionality. You can combine multiple middleware in a chained way and each middleware does not need to be aware of what is before or after it. Middleware excels at supporting asynchronous actions while avoiding boiler plate code or library dependencies like Rx. This is possible because you are able to dispatch async actions in addition to ordinary actions.

An example of a middleware is redux-thunk which enables the developer invert control by allowing the action creator to dispatch functions. You pass dispatch as an argument then make an asynchronous call. Functions that operate this way are referred to as thunks. A second example of a middleware is redux-promise. With this middleware you are able to call a Promise async action and when the promise has resolved you call a normal action.

Middleware is not built into Redux but it is fully supported to work with Redux. With this approach, there is one standardized way to extend dispatch and it is possible to create multiple middleware with varying utility.

A middleware is called by passing two arguments, store dispatch and get state function. The middleware call then returns a function which becomes the input of the next middleware method which may or may not call the next action. In the chain, the final middleware is given the actual dispatch method and the chain ends. The inputs and outputs (signature) of a middleware follow the construct shown below.

Earlier we identified logging as one of the applications of middleware. Below is an example of a logger implemented using middleware.

When using middleware, there key points to put into consideration:

The functionality provided by middleware can be manually coded by wrapping all dispatch calls but definition at a single point is better when working on large projects
Middleware has some asynchronous behavior so ensure any store enhancers you use besides ApplyMiddleware are placed before ApplyMiddleware.
Any conditional uses of middleware should be imported only when they are required. Bundling tools will leave out modules that are not required so the build is minimized

In this tutorial, we briefly reviewed actions and reducers and noted that middleware helps us implement functionality in the window between an action and a reducer. We discussed possible uses of middleware such as logging and crash reporting. We demonstrated some examples of logging without middleware and discussed their shortcomings. We demonstrated an example of logging using middleware. Finally, we identified key aspects to keep in mind when writing your middleware code.


Please enter your comment!
Please enter your name here