Introduction

In this post, I will write about Redux Middleware by viewing it in 2 different angles, one from the Object Oriented way and the other from the functional way as how it is implemented in Redux source code.

Basics of Redux

The core function of Redux is:

dispatch(action)

When the client calls dispatch(action) , it internally calls the reducer function which is a pure function (a function which does not have any side effects) whose signature is (currentstate, action) => nextstate . The client can call getState to get the current state value any time.

Middleware

As stated earlier, dispatch(action) is the core function of Redux which does the next state calculation using the reducer function. What if an application wants to log the getState() function before and after the call to dispatch(action) (or) would like to know how long it took for dispatch(action) to finish completion (or) would like to make an IO operation before calling the actual dispatch(action) ? How do we solve this problem? I was reminded of a famous quote,

“History doesn’t repeat itself but it often rhymes”

I have seen this similar problem in a couple of instances from the Object Oriented world.

Java Filters: A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.

Java IO: InputStream is an abstract class. Most concrete implementations like BufferedInputStream, GzipInputStream, ObjectInputStream, etc. have a constructor that takes an instance of the same abstract class. That’s the recognition key of the decorator pattern (this also applies to constructors taking an instance of the same interface).

Both the above examples are typically solved by a popular Gang of Four Design Pattern: Decorator Design Pattern. The basic idea of Decorator Design pattern is to augment (or) decorate the behavior of the underlying base component without the need to change/extend the base component.

So the original problem of how to augment/decorate dispatch(action) perfectly fits the bill of GoF Decorator Design Pattern. GoF Decorator design pattern provides the solution in Object Oriented way, but the general abstract motivation of using Decorator Design pattern perfectly maps to how we will solve the problem of implementing Middlewares in Redux whether in Object Oriented Way (or) Functional way.

Square Calculator

To understand middleware, we will try to view it through the lens of a very simple example:Square calculator. If the input is x the output is x*x, which basically forms the pure reducer function. Redux stores the last computed square value as its state. Additionally we would like to do 3 middleware operations:

Validate the input to make sure it is a number by calling a service (extremely hypothetical example), but in essence would like to sprinkle some impurity through an IO operation. Calculate the time taken to perform the original dispatch(action) Log the getState() before and after the original dispatch(action)

[If you look closely, not only 1 is impure because of an IO operation, even 2 and 3 are also impure since accessing the system clock to get the time and writing to the console are also impure operations.]

Implementation of Square Calculator – Object Oriented Way

As stated earlier, it is implemented using Decorator design pattern:Source Code in Java Using OO way

The UML diagram for the crux of the implementation can be shown as below:

Store globalStore; globalStore = new Thunk(new TimerStore(new LoggingStore(baseStore)));

As shown above, we are adhering to an important principle, “Code to an interface rather than an implementation” and the client ties to the interface called Store and the implementation is a fully constructed chain of decorators wrapping the base Store and the client is not aware of any of the implementation details.

Implementation of Square Calculator – Functional Way

If we look at Redux source code, the implementation of middleware uses a lot of functional constructs. I have created a slimmed down version of it to implement the logic for Square Calculator. Source Code in Javascript Using Functional way

I would like to touch upon some of the important functional programming constructs that are being employed to construct the wiring for middleware.

If you look at the above source code you will see the following important properties being employed:

In Functional Programming, functions are first class citizens. Meaning, functions can be stored in variables and can be passed around like numbers, strings etc. Use of Higher Order Functions – Functions can take functions as parameters and return functions as return values. A function that does either of those is called a higher order function. It turns out that if you want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them, higher order functions are indispensable. They’re a really powerful way of solving problems and thinking about programs. Currying – Basically if you take a simple function like adding 2 numbers, its signature for being a curried function would be var add = a => b => a + b . The caller of this function would have to do, add(1) (2) to get 3. A good analogy would be, “If I can give you a carrot for an apple and a banana, and you already gave me an apple, you just have to give me a banana and I’ll give you a carrot.” Translating to the add example, if we had called the function add with just 1 and stored the result like var addOne = add(1) . Now if the client wants to add any number to one, they can simply just do addOne(2) , so If I can give you a 3 for 1 and 2 and you already gave me 1 you just have to give me 2 and I will give you 3 Use of map function in the List – Usage of Functor Use of folds through reduce function – extremely powerful abstract API. Use of functional composition – In computer science, function composition (not to be confused with object composition) is an act or mechanism to combine simple functions to build more complicated ones. Like the usual composition of functions in mathematics, the result of each function is passed as the argument of the next, and the result of the last one is the result of the whole.

All the above 6 properties are used in just 30 lines of code 🙂

function compose(...funcs) { funcs = funcs.filter(func => typeof func === 'function') if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) } function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }

Conclusion