Redux: The Under The Hood Tour
Tags: redux, webdev, javascript, react
Published: 2019-10-01 04:33:42

_Photo by Hosea Georgeson on Unsplash_

## But wait we have React hooks now, we don't need Redux anymore, right ?
<!-- mention Redux hooks -->

If you are not a React dev, React hooks are the latest addition to React and they are absolutely awesome ⚡, but they are not replacing Redux. If you are still unconvinced, I would strongly recommend Eric Elliot article, [Do React Hooks Replace Redux?](https://medium.com/javascript-scene/do-react-hooks-replace-redux-210bab340672).

<!-- * Need revision -->

For now if you want to continue without reading Elliot article, here is the tl;dr:

- Redux is not just a library, it's architecture that proved very effective in building scalble and maintainable code.
- While you can recreate the functionalities of Redux using the createContext and React Hooks, there are no clear gains from that and you would lose access to the powerful debugging capabilities in Redux devtools.

<!-- add fun fact react-redux support customs hooks now -->

I hope that you are convinced and that you would join us in this tour. Now before we jump right in, please have a look at our brochure of functional programming concepts that you will see quite often inside Redux. _If however, you feel confident in these concepts you can skip to the start of the tour._

## TOC

- [Brochure of functional programming concepts](#brochure-of-functional-programming-concepts)
  - [Pure Functions](#pure-functions)
  - [Closures](#closures)
  - [High order functions](#high-order-functions)
  - [Currying](#currying)
  - [Function Composition](#function-composition)
- [Start of The Tour](#start-of-the-tour)
  - [First: rootReducer, the maker of the new state](#first-raw-rootreducer-endraw-the-maker-of-the-new-state)
  - [Second: createStore, the store maker](#second-raw-createstore-endraw-the-store-maker)
  - [Third: middleWares, the ones in the middle](#third-raw-middlewares-endraw-the-ones-in-the-middle)
  - [Fourth: enhancers, Augmenting createStore](#fourth-raw-enhancers-endraw-augmenting-raw-createstore-endraw-)
    - [The applyMiddleWare](#the-raw-applymiddleware-endraw-)
- [The Gift shop](#the-gift-shop)

## Brochure of functional programming concepts

We are not going to try and give an exhaustive explanation of those concepts here as I believe it would be futile to try and jam all of those in a single article. However, I'll try to explain just enough so you can gain the most from this article.

### Pure Functions

- Functions which their return value is determained by the arguments passed to them.
- They don't access or modify values outside of their scope.

### Closures

Closures are created at the **creation** of new functions and they allow those functions to access the outer scope.

```javascript
function outer() {
  const savedInClosure = true;
  return function() {
    if (savedInClosure) {
      console.log('I always have closure');
    }
  };
}

const doYouHaveClosure = outer();
doYouHaveClosure(); // 'I always have closure'
```

### High order functions

Functions that receive functions as an argument and/or return another function. Also, Yup the code above is a high order function, well done for noticing 😉.

### Currying

Currying is the technique of taking a function that takes multiple arguments and transforming it into a series of functions that takes one argument at a time. Now, you might scream to yourself why would I ever want to do that. Well the simple answer is "specialized Functions and separation of complexity". Let's have a look at the canonical example of currying:

```javascript
// Before currying
const add_notCurrying = (x, y) => x + y;

// after currying
const add_currying = x => y => x + y;

// specialize functions
const add2 = add_currying(2);

add2(8); // 10
```

Now say your manager comes to you and tell you, "the add functions must do a bunch of checks and API calls before committing the first argument and must do totally different checks and API calls to commit the second argument". In the uncurried version you would have to jam all that complexity into one functions, while on the curried version of `add` you can separate it.

### Function Composition

Function composition is the process combining functions to build more sophisticated ones, and yes in the examples above we've already done some function composition. However, the techniques that I want to explain here is the one that might give you headache first time you see it:

```javascript
const myFuncs = [func1, func2, func3, func4];

const compose = arr => arr.reduce((a, b) => (...args) => a(b(...args)));

const chain = compose(myFuncs);
```

**WAAAAAAAAIT**...., Now trust me if you don't have experience in functional programming, like I was when I first saw this, having a reaction like "🤬🤬🤬🤬" is in my opinion the healthiest response you can have. Unless you are well versed in functional programming, this will not be intuitive and it might takes time for it to click in your mind, but. For now, know that all compose does is help us get to something like this function.

```javascript
const composed = (...args) => func1(func2(func3(func4(...args))));
```

As you can see the final function we get from compose, calls the functions in the array right-to-left and passing the return of each function as the argument to the previous one. Now with that mental framework in mind try to take a look at a refactored version from the code above.

```javascript
const myFuncs = [
  () => {
    console.log(1);
  },
  () => {
    console.log(2);
  },
  () => {
    console.log(3);
  },
  () => {
    console.log(4);
  }
];

let chain = myFuncs[0];

for (let index = 1; index < myFuncs.length; index++) {
  const currentRingInTheChain = myFuncs[index];

  // This is necessary to avoid recursion. Basically we storing different instances of functionsChainSoFar in closure scopes
  const functionsChainSoFar = chain;

  chain = (...args) => functionsChainSoFar(currentRingInTheChain(...args));
}

chain(); // 4 , 3, 2, 1
```

I hope that clarified what `compose` does but if you are still not 100% sure, don't worry too much. Again this might take time and it does require a mental shift.

BONSUS ROUND: what do you think the following code will log?.

```javascript
const myFuncs = [
  func => () => {
    console.log(1);
    func();
  },
  func => () => {
    console.log(2);
    func();
  },
  func => () => {
    console.log(3);
    func();
  },
  func => () => {
    console.log(4);
    func();
  }
];

const hakuna = () => console.log('Mattata');

const secret = compose(myFuncs)(hakuna);

secret(); // what do you think this will log?
```

Give it a go but if you get stuck don't worry will revisit this again in the article.

## Start of The Tour

The best way to start the tour is to see how we are creating a Redux store and what are the pieces that plays a part in that. So let's have a look at this sample from the docs.

```javascript
import { applyMiddleware, createStore } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';

import monitorReducersEnhancer from './enhancers/monitorReducers';
import loggerMiddleware from './middleware/logger';
import rootReducer from './reducers';

export default function configureStore(preloadedState) {
  const middlewares = [loggerMiddleware, thunkMiddleware];
  const middlewareEnhancer = applyMiddleware(...middlewares);

  const enhancers = [middlewareEnhancer, monitorReducersEnhancer];
  const composedEnhancers = composeWithDevTools(...enhancers);

  const store = createStore(rootReducer, preloadedState, composedEnhancers);

  return store;
}
```

There is a lot going on here, we are using redux-thunk, attaching the redux-devtools-extensions and a lot more. So, let's divide and conquer and separate the code above into four domains.

1. **The `reducers`**
2. **The `createStore` functions**
3. **The `enhancers`**
4. **The `middlewares`**

### First: `rootReducer`, the maker of the new state

The `rootReducer` function is the first of the three arguments that `createStore` takes and chances are you already know that redux `reducers` are functions that takes the current state and an action and return a new state. You might also already know that the `reducers` must be **pure functions**.
However, have you ever wondered _"why reducers have to be pure functions?"_ 🤔. Well there is a very good reason, but unfortunately, there isn't a piece of code that I can point to and tell you _"if the is NOT pure function it will ALWAYS break"_. Yet the fact that `reducers` must be pure functions is at the heart of what Redux aim to be, and that is **"a state store with predictable state mutation"**. Redux, achieves that by adehering to three self-imposed principles:

- A Single source of truth
- State is read-only
- Changes to the state are made with pure functions

If that didn't click immediately in your mind don't worry we will see those principles again in this article.

So, reducers are pure functions. They take the current state and an action as arguments and return a **new state object**, got it 👍. _But how about `combineReducers`, how does that magical function work_. Well `combineReducers` is an awesome utility function that help us keep our code modular, but really there is nothing magical about it. `combineReducers` is a high order function and all what it does is:

- Extract an array from the reducer object passed into it _(note that the reducer keys match the shape of the state tree)_.
- Return a new `reducer` function.
  - This function will make the next state by looping over the array of reducers keys and call the coresponding `reducer`.
  - Finally, it will return the next state.

Take a look at the trim down version of `combineReducers`:

```javascript
const reducers = {
  someState: reducerOfSomeState,
  anotherState: reducerOfAnotherState
};

function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers);

  return function combinedReducer(state = {}, action) {
    const nextState = {};
    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i];
      const reducer = reducers[key];
      const previousStateForKey = state[key];
      const nextStateForKey = reducer(previousStateForKey, action);

      nextState[key] = nextStateForKey;
    }
    return nextState;
  };
}

const rootReducer = combineReducers(reducers);
```

Finally, there is an important insight that you might already noticed by looking at `combineReducers`, which is, **each time the `rootReducers` gets called all of the `reducers` in your app will be called** to create the next state.

<!-- *When introducing reducers hit on the point of immutability -->

### Second: `createStore`, the store maker

In it's simplest form `createStore` return a state object and few methods. However it also accepts extra arguments that _enhances_ 😉 the store but more on that later. For now let's make sure we understand a simpler version of `createStore`.

We've already seen the **three principles** that redux is built on. Now, let's take another look at them and try building our own redux replica 🛠:

- **A Single source of truth** ≈ we should have a single store object.
- **State is read-only** ≈ state object should not be mutated directly, instead changes should be described and emitted using a method. _(If don't understand how we got that from "state is read-only" then that's fair after all it's only four words. However, the [docs](https://redux.js.org/introduction/three-principles#state-is-read-only) elaborate on the point and make the intention of the principle clear.)_
- **Changes are made with pure functions** ≈ reducers have to be pure functions.

Adhering to the principles above our Redux replica might look something like this:

```javascript
// An action to initialize our state
const ActionTypes = {
  INIT: `@@redux/INIT${Math.random()
    .toString(36)
    .substring(7)}`
};

function createStore(rootReducer, initialState) {
  let currentState = initialState;

  const dispatch = action => {
    currentState = rootReducer(action);
  };

  const getState = () => currentState;

  // setting the initial state tree.
  dispatch({ type: ActionTypes.INIT });
  return {
    dispatch,
    getState
  };
}

const myAwesomeStore = createStore(rootReducer, {});
```

Those few lines might not look like much, but they are equivalent to the core functionlites of Redux. Of course, Redux adds some checks to help developers avoid stupid mistakes like calling dispatch from inside a reducer or not calling `dispatch` with a plain object. Also our replica doesn't support `middleware` or `enhancers`, yet at least.

### Third: `middleWares`, the ones in the middle

<!-- *what are they -->

I knowwwwwwww 🤯,
Ok ok but seriously though, it's helpfully to think of them conceptually as a middleman between the `dispatcher` and the the `rootReducer`. _SPOILER ALERT: At the the Enhancer section we will see that it's a little bit more complected than that._
Because actions go through middleware, there they can be changed, canceled or really anything else. There is a lot nuance in how to use middleware effectively, but in this article we will only focus on how they work inside Redux. So let's see that by examining what is probably the simplest middleware you will ever see.

```javascript
const middledWare = ({ dispatch, getState }) => next => action => {
  if (typeof action === 'function') {
    return action(dispatch, getState, extraArgument);
  }
  return next(action);
};
```

If you are eyes skipped the first line and immediately went to the body of the final function you might have seen that the logic is straightforward. However, once your your eyes aim back at the first line, bells in your head should start ringing CURRYING. Also, If you feel at all confused by this, don't disheartened because you are not alone, In fact this questions is one of FAQs in the docs [Why does the middleware signature use currying?](https://redux.js.org/faq/design-decisions#why-does-the-middleware-signature-use-currying). In the [next section](#fourth-raw-enhancers-endraw-augmenting-raw-createstore-endraw-) we will see how this function signutre is being used by Redux inside `applyMiddleware`, for now just remember the following from the middleware signature above.

1. the first function will get called with the an object that has two properties `dispatch` and `getState` (the middleWareApi).
2. The second function gets called with `next` (the next middleWare).
3. The final function _act as_ a `dispatch` and it gets called with an action.

FUN FACT 🤓: You might not have noticed it, but the code above is actully the [source for redux-thunk.](https://github.com/reduxjs/redux-thunk/blob/master/src/index.js)

### Fourth: `enhancers`, Augmenting `createStore`

<!-- Enhancers are higher order functions -->

As you might have already guessed, `enhancers` are high order functions that take `createStore` and return a new **enhanced** version of `createStore`. Take a look a this sample implementation.

```javascript
const ourAwesomeEnhancer = createStore => (reducer, initialState, enhancer) => {
  const store = createStore(monitoredReducer, initialState, enhancer);
  //  add enhancer logic

  return {
    ...store
    //   you can override the some store properties or add new ones
  };
};
```

While it's rare that you might need to craft your own `enhancers`, you are likely already using at least one, `applyMiddleware`. Oh Yes, this is might be shocking to some but **the notion of `middlewares` is not in Redux `createStore`**. We add middlewares capabilities to our store by using the only `enhancer` that ships with Redux `applyMiddleware`.

_To be specific the actually enhancer is the returned function from `applyMiddleware` but they are referenced interchangeably in the docs._

The `enhancer` function is first called from inside `createStore` and there isn't anything magical or overly complected. As you will soon see soon. However before we see the code, we need to address an urgent problem 🚧. Because `enhancers` take `createStore` and returned enhanced version of `createStore`, you can see how using those terms to explain the mechanics of the `enhancer` can get convoluted very quickly. As such for the purposes of this section I'm introducing what I dubbed as **placeholders terms**:

- The **originalStoreMaker**: the `createStore` function that you can import from Redux.
- The **storeMaker**: any function that has the same signature as the **original storeMaker** (accepts the same arguments and return the same API).

Alright then now let's see some code. Take a look at our Redux replica from above, now modified to accept `enhancer`.

```javascript
function createStore(rootReducer, initialState, enhancer) {
  let currentState = initialState;

  // Now accepts enhancers
  if (typeof enhancer !== 'undefined' && typeof enhancer === 'function') {
    return enhancer(createStore)(reducer, preloadedState);
  }

  const dispatch = action => {
    currentState = rootReducer(action);
  };

  const getState = () => currentState;

  // setting the initial state tree.
  dispatch({ type: ActionTypes.INIT });
  return {
    dispatch,
    getState
  };
}
```

<!-- *Maybe not clear -->

As I said nothing magical. It's just function that takes a **storeMaker** and return an enhanced **storeMaker**. Of course that's not to say that `enhancer` can't be complex. It's to say that the complexity of an `enhancer` is encapsulated inside it and determined by what it try to achieve AND not by how it interact with a **storeMaker**. This subtle distinction is important as we in the rest of this section examine the implementation of the most widely used `enhancer` in Redux, `applyMiddleware`.

#### The `applyMiddleWare`

```javascript
function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this 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
    };
  };
}
```

OK that was the whole thing now let's unpack it. Let's first quickly understand the curring part at the top. What we really need to know here is what arguments will those functions gets called with, luckily for us we already know that:

- `applyMiddleware` takes **`middlewares`** return an `enhancer`.
- `enhancers` take a **storeMaker** and return an enhanced **storeMaker**.

From that we can bring our focus back to the body of the final function and noting what it has in closure.

```javascript
// In closure: [middlewares], createStore

// This final function is a storeMaker
(...args) => {
  const store = createStore(...args);
  let dispatch = () => {
    throw new Error(
      'Dispatching while constructing your middleware is not allowed. ' +
        'Other middleware would not be applied to this 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
  };
};
```

Much better, now Somewhere in the code this **storeMaker** will get called with `rootReducer` and `initialState`. Jumping inside the function, the first two lines create the store and assign a function to a variable named `dispatch`. As the error message say this is done to prevent developer from accidentally calling `dispach` inside a **storeMaker**.

```javascript
// In closure: middlewares and the original createStore.

// + more code above
const store = createStore(...args);
let dispatch = () => {
  throw new Error(
    'Dispatching while constructing your middleware is not allowed. ' +
      'Other middleware would not be applied to this dispatch.'
  );
};
// + more code below
```

Before looking at the second piece of code try to **remember the signature of a `middleware` in Redux** [that we've seen before](#third-raw-middlewares-endraw-the-ones-in-the-middle). Here the first of those curried function of each `middleware` gets called. After this part of code we will get an array of functions where each has a reference in their closure to the `middleWareAPI` object.

```javascript
// In closure: middlewares and the original createStore.

// + more code below
const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
};

const chain = middlewares.map(middleware => middleware(middlewareAPI));
// + more code below
```

Brace yourself, the next line is probably the most intimidating part of the code. Largely because of the `compose` function. Nonetheless, give it a go 💪 and take this hint: **all the functions in the `chain` variable return a function.**

```javascript
// In closure: middlewares and the original createStore.

// + more code below
dispatch = compose(...chain)(store.dispatch);
// + more code below
```

If you went through our Brochure of functional programming concepts, seeing the code above might rang few bells inside your head. Because this code look very similar to the code from the BONUS ROUND in the [function composition sub-section](#function-composition). Speaking of which, what did you guess the code from there will log?....

well let's have another look.

```javascript
const myFuncs = [
  func => () => {
    console.log(1);
    func();
  },
  func => () => {
    console.log(2);
    func();
  },
  func => () => {
    console.log(3);
    func();
  },
  func => () => {
    console.log(4);
    func();
  }
];

const hakuna = () => console.log('Mattata');

const secret = compose(myFuncs)(hakuna);

secret(); // 1, 2, 3, 4, Matata
```

Yes, if you tried to run the code in the console, you've seen that it logs _1, 2, 3, 4, Matata_. The code seems to have ran left-to-right. Except after the returned function from `compose` gets called with `hakuan`, we don't have an array anymore!. Where is the left-to-right coming from?? It's because of Closures and callbacks. Ok I'm guessing that wasn't super helpful 😅. No worries though, I'll try to explain a little better but first to avoid confusion, I'm going to need once again to introduce new **placeholders terms**.

- **level1Func**: any function inside the `myFuncs` array.
- **level2Func**: any function that is returned by a **level1Func**.

Alright, let's recap what is it that we want to achieve. We want somehow for all of **level2Func** to run in order from left-to-right. We can see in the array that each **level1Func** takes a callback as an argument and then that callback gets called inside it's **level2Func**. So it's seems that we can achieve our goal if somehow each **level1Func** got called with the **next** **level2Func**.

OK Ok gears are turning ⚙⚙ we are closing into something. We know by now that compose will return a function that will call functions right-to-left and passing each return to the previous function in the array. But god it's too hard running that code inside my mind 😵. Maybe if we saw how that would look like differently.

```javascript
const composed = (...args) => func1(func2(func3(func4(...args))));
```

AHA!, As `composed` gets called and the functions gets called right-to-left, each **level1func** will be called by the **next** **level2func**. Well done You got it 👏. That is exactly how we end-up with a function that resemble a chain in how it runs left-to-right. The last thing to point out and hammer home is that `hakuna` function is the first argument that gets passed by composed and as such **it's the last function in the chain**

Now with this new found understanding let's look back at the line code from `applyMiddleware`. I hope you can see by now how the chain is made, that each middleWare will calls the **next** one and that the last function in the chain is `store.dispatch` which sets the new state (but NOT create it) to the store.

```javascript
// In closure: middlewares and the original createStore.

// + more code below
dispatch = compose(...chain)(store.dispatch);
// + more code below
```

<!-- * this line requires a lot more explication -->

Finally, because this is after all a **storeMaker** function we return the store and of course override the `dispach` property.

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

<!-- *what happens in case of multiple enhancers -->
<!-- * maybe don't bother with compose to make things easier and added it in the prolog -->
<!-- * get rid of prolog and just say that they exist -->

## The Gift shop

The above is everything about how the core of Redux works. There are few more methods that ships with Redux and while they will not alter your understanding of how Redux works, they are worth mentioning. Here is a quick list.

- **replaceReducer**: Give you the ability replace the rootReducer of the store. Interestingly in some setups you can use it to add new reducers rather than just replacing the whole `rootReducer`.
- **subscribe**: Give you the ablity to pass a callback that will get called after any action gets dispatched.
- **observable**: Can be used in libraries like RxJS. Also allowes you to subscribe to changes.

Congratulation you made it 🎊🎊👏👏. Now you understand how Redux works under the hood and hopefully have gained an appreciation for the power functional programming.