# Redux Middleware Explained

Whether you're looking to add some customization to your Redux workflow or just looking to understand how Redux works a little better, it's great to know exactly what Redux is doing under the hood with middleware.

I've been going down this rabbit hole trying to write my own Redux middleware so I thought I'd share my findings.

Fortunately for us, Redux turns out to be fairly simple, so let's take a closer look at the Redux source code and try to figure out what's going on.

I'll pull examples from the Redux source code and type definitions, but I'll be stripping it down for simplicity, mostly removing some error-handling and subscription management code (subscriptions aren't particularly relevant for us here). Let's start with the primitives and work our way up.

*Examples are based on Redux `4.0.4`*

## What is an `Action`?

```typescript
export interface Action<T = any> {
  type: T
}
```

Most strictly, an `Action` is an object with a field `type`. More usefully, an `Action` describes *what* change should be made to the application state.

## What is a `Reducer`?

```typescript
export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S
```

A `Reducer` is a function that, given an existing state and an `Action`, produces the state that should exist after the `Action` has been applied. If an `Action` describes *what* changes should be made, a `Reducer` describes *how* that change should be made.

## What is a `Store`?

Very simply, it's just an object that provides us a way to get the current application state (`getState()`) and provides a `dispatch` function that allows us to send `Action`s to our `Reducer`s. A simplified interface would be something like this:

```typescript
interface Store<S = any, A extends Action = AnyAction> {
  dispatch: Dispatch<A>
  getState(): S
}
```

We create our Redux store using the `createStore` function, which, well, creates our `Store`. More importantly, it contains some important variables that will get used by functions like `dispatch`.

```javascript
function createStore(reducer, preloadedState, enhancer) {
  ...
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  ...
}
```

What's important is that `dispatch` and other functions are also created in `createStore`, meaning that these variables are closed over, i.e. `dispatch` will have access to these variables, and we'll see how it uses them next.

## What does `dispatch` actually do?

I'll be honest, I was a little surprised at just how simple the `dispatch` function is when I first saw it (though I don't really know what I expected). You'll see that is uses those variables defined in `createStore` from before. Here's a stripped down version, showing just the meat of it:

```javascript
function dispatch(action) {
  try {
    isDispatching = true
    currentState = currentReducer(currentState, action)
  } finally {
    isDispatching = false
  }

  return action
```

Well, that's about it. It reassigns `currentState` to the new application state the reducer spit out in response to an `Action`.

Keep in mind, there is only *one* `Reducer` – don't let the variable name `currentReducer` throw you off. It's only "current", because of some functionality Redux provides for dynamically loading reducers that isn't relevant for us here. Speaking of which, how does Redux make all our different reducers act as one?

## How does Redux combine reducers?

Redux provides the `combineReducers` function, which takes a object-map of sub-reducers and returns a single function, i.e. a `Reducer`, that invokes all of them. Here's a simplified version of `combineReducers`.

```javascript
function combineReducers(reducers) {
  return (state = {}, action) => {
    let hasChanged = false
    const nextState = {}

    for (const reducerKey in reducers) {
      const reducer = reducers[reducerKey]
      const previousStateForKey = state[reducerKey]
      const nextStateForKey = reducer(previousStateForKey, action)
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}
```

Again, just like `dispatch`, this is actually pretty simple. We go through all the sub-reducers we have, pass it existing state and the action, and collect all those results into a new state object.

## What does a Redux `Middleware` look like?

Middleware is where this gets a little more complicated (which is why it was important to go over the building blocks!).

Middleware allows us to add all sorts of functionality to Redux between the points when an action is first dispatched to when it's sent to the reducer. It also allows us to enhance the `dispatch` function to add to its capabilities.

Let's examine all the relevant code.

### `MiddlewareAPI`

```typescript
export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> {
  dispatch: D
  getState(): S
}
```

A `MiddlewareAPI` is what gives our middleware access to `dispatch` and a way to get the current state. Simple enough.

### `Middleware`

```typescript
export interface Middleware<
  DispatchExt = {},
  S = any,
  D extends Dispatch = Dispatch
> {
  (api: MiddlewareAPI<D, S>): (
    next: Dispatch<AnyAction>
  ) => (action: any) => any
}
```

This type definition might be a little hard to follow, but notice that the last function in the signature just takes in an action, i.e. the same as `dispatch`.

I think the best way to think about it is that every middleware is actually just a `dispatch` that has a handle to the *next* dispatch in the chain. So really, our middlewares are just custom `dispatch`s that handle the action in some way and then pass the action off to the next dispatch. You may need to stew on that for a minute and make sure you understand it.

The reason this signature needs to be curried is that the custom `dispatch` we create needs access to the `MiddlewareAPI` and a handle to the next middleware/dispatch in our middleware chain. All that happens in `applyMiddleware`.

### `applyMiddleware`

```typescript
export function applyMiddleware(...middlewares: Array<Middleware>): StoreEnhancer
```

Basic type definition.

```javascript
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
```

*I'm assuming you have a baseline understanding of curried functions in JavaScript here* I think a helpful way to conceptualize deeply curried functions is as just a single function with multiple 'layers' of parameters. The 'layers' get 'peeled off' when you apply a argument to it, which allows us to share common data between multiple curried functions. In this way, a `Middleware` is a curried function with three layers – an `APIMiddleware`, a `Dispatch`, and finally an `Action`. `applyMiddleware` 'peels off' two layers of our `Middleware`s, creating a single function that becomes our Redux Store's `dispatch`.

To 'peel off' the first layer, we first create a `MiddlewareAPI` from our newly created store and give all our middlewares access to it with `middlewares.map(middleware => middleware(middlewareAPI))`.

To 'peel off' the second layer, we chain our `Middleware`s together with the `compose` function. `compose` serially chains multiple functions together into a single function. It's actually not terribly complicated, but it's easier to understand by example: `compose(F, G, H)` turns into `(...args) => F(G(H(...args)))`. (Note that in this chain, the parameters that `H` accepts become the parameters that our newly created chain accepts.) Finally, we call this composed `Middleware` chain with the default `dispatch` from our store.

With the second layer peeled off, now our `middlewares` are just normal `dispatch`s (`Action => any`) that have a handle to `next` i.e. the next `dispatch` in the chain. (That means we have to make sure our `Middleware` calls `next` at some point, or the dispatch won't make it to the end i.e. our `Reducer`!)

I'll try my best to illustrate that process the best I can.

```javascript
const chain = middlewares.map(middleware => middleware(middlewareAPI))
const composedChain = compose(...chain)
dispatch = composedChain(store.dispatch)
```

Calling our composed chain with `store.dispatch` will make `store.dispatch` the final `dispatch` in the chain. Here's some pseudocode that outlines how that application would go.

```javascript
H(store.dispatch):
  (action: Action) => {
    // custom stuff...
    store.dispatch(action)
  }
    G(H):
      (action: Action) => {
        // custom stuff...
        H(action)
      }
        F(G):
          (action: Action) => { // Our new Dispatch!
            // custom stuff...
            G(action)
          }
```

And now here's what our final `dispatch` will look like. This is, again, pseudocode to illustrate how the data flows, not the definitions of the functions.

```javascript
(action: Action) => {
  F(action) ->
    // custom stuff
    G(action) ->
      // custom stuff
      H(action) ->
        // custom stuff
        store.dispatch(action)
}
```

The final bit of `applyMiddleware` is returning our `store`, but with `dispatch` overridden to be our custom `dispatch`.

```javascript
return {
  ...store,
  dispatch
}
```

And finally, we've created our Redux store with our middleware applied!

## Try It Out

I hope this look into Redux has given you what you need to start tinkering yourself. I did most of this research while figuring out how to create a [custom middleware of my own](https://alanqthomas.io). It's certainly within reach to try it yourself – maybe a logging framework, maybe a network call abstraction layer, maybe something even wilder. Go ahead and tinker with it and see what you can come up with.
