Redux middleware as pure functions
Tags: javascript, react, redux, patterns
Published: 2019-03-08 19:16:25

---
title: Redux middleware as pure functions
published: true
description: Handle side-effects and business logic with pure JS functions
tags: javascript, react, redux, patterns
---
## Source of inspiration

I decided to write this article after seeing [this video](https://www.youtube.com/watch?v=5gl3cCB_26M) by Nir Kaufman. Do not be scared by the title, it's within reach of any developer with a minimum of imagination and sense of humor.

## What is a Redux middleware?

A function. Really, nothing more.

Actually, it's a function that returns a function that returns a function that does something.

Something like this:

```javascript
const middleware = () => () => () => {
  // Do something
}
```

It looks a lot like a normal Javascript's closure, isn't it?

Let's review it populated by some parameters:

```javascript
const middleware = (store) => (next) => (action) => {
  next(action)
}
```

Let's analyze the parameters that are passed to it:

- `store`: the actual store of Redux, from which we can deconstruct `getState` and `dispatch`
- `next`: the next middleware
- `action`: the action that has been dispatched

It is *necessary* to call `next(action)`, otherwise the flow will be blocked (this does not have to be a negative thing).

## Pros and cons about using one or more custom middleware

### Pros

- Freedom of implementation
- No pattern constraints, just a few suggestions
- No bottlenecks hidden somewhere, just pure functions

### Cons

- Freedom of implementation
- No pattern constraints, just a few suggestions
- No bottlenecks hidden somewhere, just pure functions

No, you didn't become crazy: I deliberately returned the same points. This free approach is very powerful but very dangerous if not used in the right way: you could find yourself managing performance drops only due to a poor implementation or management of a side-effect.

The classic scene where the developer plays both the **sheriff** and **bandit** roles.

Remember the words of Uncle Ben:

![](https://media.giphy.com/media/MCZ39lz83o5lC/giphy.gif)

## Why should I build one or more middleware, then?

Well, you don't really have to.

The alternatives are varied and vast: just think of `redux-thunk`, `redux-saga` and many others. They are all middleware in turn, do their work and some of them are also very performing.

That said, if you still think that you want to use a library, I will not stop you. Actually, I'm a big fan of `redux-saga`!

## Just a few suggestions

Let's now see together, referencing to Nir's suggestions, some patterns that can be used immediately.

![](https://image.slidesharecdn.com/reduxpattens-180423104117/95/redux-pattens-jsheroes-2018-35-638.jpg?cb=1524480297)

### Filter
```javascript
const middleware = (store) => (next) => (action) => {
  // Avery action with type BAD_ACTION will be removed from the flow
  if (action.type === 'BAD_ACTION') return

  next(action)
}
```
### Map
```javascript
const middleware = ({ dispatch }) => (next) => (action) => {
  // We don't want to remove this action from the flow
  next(action)
  
  if (action.type === 'ACTION_FROM') {
    // Instead, we want to fire a side-effect
    dispatch({ type: 'ACTION_TO' })
  }
}
```
### Split
```javascript
const middleware = ({ dispatch }) => (next) => (action) => {
  // We don't want to remove this action from the flow
  next(action)
  
  if (action.type === 'ACTION_COMPOSED') {
    dispatch({ type: 'ACTION_FIRST' })
    dispatch({ type: 'ACTION_SECOND' })
  }
}
```
### Compose / Aggregate

Compose and Aggregate are similar in their behavior.

To differentiate them we could simply say that the first logic expects more actions of the same type and then generates a unique side-effect, similar to a buffer; the second logic expects actions of different types.

To achieve this, we need to introduce the concept of middleware status. Thus creating what I like to call **stateful-middleware**.

Let's see how to implement this pattern in order to generate a side-effect, without blocking the flow.
```javascript
// We are not defining a middleware, we are defining a middleware's factory
const makeMiddleware = (waitFor, waitTimes, handler) => {
  let actions = []

  // Here we are returning a middleware
  return (store) => (next) => (action) => {
    next(action) // We aren't blocking the flow
  
    if (action.type === waitFor) {
      actions.push(action)

      if (actions.length === waitTimes) {
        // We then pass to the handler all the actions
        handler(store, actions)

        // We may then reset the "state" to start again
        actions = []
      }
    }
  }
}
```
Or using a blocking approach.
```javascript
// We are not defining a middleware, we are defining a middleware's factory
const makeMiddleware = (waitFor, waitTimes, handler) => {
  let actions = []

  // Here we are returning a middleware
  return (store) => (next) => (action) => {
    if (action.type === waitFor) {
      actions.push(action)

      if (actions.length === waitTimes) {
        // We then pass to the handler all the actions blocked before
        handler(store, actions)

        // We may then reset the "state" to start again
        actions = []
      }
    } else {
      next(action)// We are blocking the flow
    }
  }
}
```
Deciding to block or not the flow is up to you, both cases could be useful to solve different problems.

To go from Compose to Aggregate it will be enough to allow the factory to verify that the action that it's been intercepted is among those that should be waited for.
```javascript
// snip
// Where waitFor is an array of action types like ['ACTION_A', 'ACTION_B', 'ACTION_C']
// Boolean is unecessary, just for clarity
if (Boolean(~waitFor.indexOf(action.type))) { /* snip */ }
// snip
```
### Enrich

This pattern turned very useful for me for adding, for example, a timestamp to some actions.
```javascript
const middleware = (store) => (next) => (action) => {
  if (action.type === 'ACTION_TO_ENRICH') {
    next({
      ...action,
      payload: {
        ...action.payload,
        '@': Date.now(),
      }
    })
  } else {
    next(action)
  }
}
```
### Normalize
```javascript
const middleware = (store) => (next) => (action) => {
  if (action.type === 'ACTION_TO_NORMALIZE') {
    // Clone payload, it will be less painful to modify it
    const payload = { ...action.payload }
    if (typeof payload.postId === 'number') payload.postId = payload.postId.toString()
    
    next({
      ...action,
      payload,
    })
  } else {
    next(action)
  }
}
```
### Translate

Indeed, I do not think I have a real example for this pattern. If you can think of a better one, please let me know in the comments!
```javascript
const middleware = (store) => (next) => (action) => {
  if (action.type === 'ACTION_TO_TRANSLATE') {
    next({
      ...action,
      type: 'ACTION_TRANSLATED',
    })
  } else {
    next(action)
  }
}
```
## How to integrate these newly created middleware

I will not go into the merits of how to create a Redux store, you've done it thousands of times. Rather, I will show you how to apply these middleware to the newly created store.

Do not take this example literally, there are many ways to handle more and more middleware within the codebase. This approach is the simplest that came to my mind.
```javascript
import { createStore, applyMiddleware, compose } from 'redux'

import rootReducer from './rootReducer'
import initialState from './initialState'
import { someFilterMiddleware, someMapMiddleware, someComposeMiddleware } from './middlewares'

const customMiddlewares = [
  someFilterMiddleware,
  someMapMiddleware,
  someComposeMiddleware('ACTION_TO_WAIT', 2, (store, actions) => console.log(actions))
]

const configureStore = () => {
  // Spread them as arguments for applyMiddleware
  const middlewares = applyMiddleware(...customMiddlewares)

  const store = createStore(
    rootReducer,
    initialState,
    compose(middlewares),
  )

  return store
}

export default configureStore
```
### Attention!

All of this is very nice, but the fact remains that, the more middleware created, the more the number of functions through which an action must pass before reaching its destination increases. Rather, you might prefer a mono-middleware approach that can handle a series of major cases if, and only if, some criteria are met: something similar to `redux-saga`.

`redux-saga` is structured in a single middleware, which runs a generator-runner over and over, as long as there are actions to be interpreted or effects to be dispatched. We will not go further.

## What's the point?

The point is that you do not really need a particularly complex or engineered library to manage application side-effects or business logic in a modular way.

Do you need to manage a login? Create a middleware. Don't you need it anymore? Disconnect the middleware from the flow and you won't have to act elsewhere.

There is no more versatile thing than being able to use any pattern, conventions or browser API to achieve a goal.

You can use closures, factory, iterator (why not), setTimeout, setInterval or the newest [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback) API.

Again, I am not saying that this approach can completely replace a single and more structured middleware. After all, if certain libraries were born, they had excellent reasons. I just wanted to share with you a way to handle some logic that was different from the usual.

## Thanks everybody!

Thank you for reading this article until the end! If you liked it, leave a 🦄!

If you do not agree with what I wrote, leave a comment and share some ideas!