How Redux works ? (Only HTML & Pure JS)
Tags: react, redux, javascript
Published: 2021-01-29 04:15:42

This is a code example of Redux with only HTML & pure JavaScript. [Code sandbox](https://codesandbox.io/s/github/reduxjs/redux/tree/master/examples/counter-vanilla)
```javascript
<!DOCTYPE html>
<html>
  <head>
    <title>Redux basic example</title>
    <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
  </head>
  <body>
    <div>
      <p>
        Clicked: <span id="value">0</span> times
        <button id="increment">+</button>
        <button id="decrement">-</button>
        <button id="incrementIfOdd">Increment if odd</button>
        <button id="incrementAsync">Increment async</button>
      </p>
    </div>
    <script>
      function counter(state, action) {
        if (typeof state === 'undefined') {
          return 0
        }

        switch (action.type) {
          case 'INCREMENT':
            return state + 1
          case 'DECREMENT':
            return state - 1
          default:
            return state
        }
      }

      var store = Redux.createStore(counter)
      var valueEl = document.getElementById('value')

      function render() {
        valueEl.innerHTML = store.getState().toString()
      }

      render()
      store.subscribe(render)

      document.getElementById('increment')
        .addEventListener('click', function () {
          store.dispatch({ type: 'INCREMENT' })
        })

      document.getElementById('decrement')
        .addEventListener('click', function () {
          store.dispatch({ type: 'DECREMENT' })
        })

      document.getElementById('incrementIfOdd')
        .addEventListener('click', function () {
          if (store.getState() % 2 !== 0) {
            store.dispatch({ type: 'INCREMENT' })
          }
        })

      document.getElementById('incrementAsync')
        .addEventListener('click', function () {
          setTimeout(function () {
            store.dispatch({ type: 'INCREMENT' })
          }, 1000)
        })
    </script>
  </body>
</html>
```
The webpage looks like this
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pv5xw09z5mgajl5d4gyg.png)

1. `createStore` & `counterReducer`

```javascript
// Counter reducer
function counterReducer(state, action) {
	if (typeof state === 'undefined') {
		return 0;
	}
	switch (action.type) {
		case 'INCREMENT':
			return state + 1;
		case 'DECREMENT':
			return state - 1;
		default:
			return state;
	}
}
// Create store
var store = Redux.createStore(counterReducer);
```
* `createStore` receives a `counterReducer` function as a param and return an object called store.
* This is the diagram of createStore function with mental model as a class.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d0wre1y2pper4p1zdufu.png)

Here is [simplified version of `createStore` in redux source code](https://gist.github.com/gaearon/ffd88b0e4f00b22c3159):
```javascript
function createStore(reducer, initialState) {
  var currentReducer = reducer;
  var currentState = initialState;
  var listeners = [];
  var isDispatching = false;

  function getState() {
    return currentState;
  }

  function subscribe(listener) {
    listeners.push(listener);

    return function unsubscribe() {
      var index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  function dispatch(action) {
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.');
    }

    try {
      isDispatching = true;
      currentState = currentReducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    listeners.slice().forEach(listener => listener());
    return action;
  }

  function replaceReducer(nextReducer) {
    currentReducer = nextReducer;
    dispatch({ type: '@@redux/INIT' });
  }

  dispatch({ type: '@@redux/INIT' });

  return { dispatch, subscribe, getState, replaceReducer };
}
```

* `currentReducer` = `counterReducer`
* `currentState` = `preloadedSate`
* When store is created, it initially `dispatch` with action type is `'@@redux/INIT'` so that every reducer returns their initial state. In case `counterReducer`, it returns `0`

### What happens inside `dispatch` function ?

```javascript
// Dispatch function inside Redux store
function dispatch(action: A) {    
	currentState = currentReducer(currentState, action)
	const listeners = (currentListeners = nextListeners)
	for (let i = 0; i < listeners.length; i++) {
		const listener = listeners[i]
		listener()
	}
	return action
}
```

- The function `currentReducer` is called which is `counterReducer`
- Because action type is `@@redux/INIT` and `currentState` is `undefined`, so `counterReducer` returns `0` as default value which is the initial state of the store.
- Now, `currentState` is `0`
- After updating the state with initial value, it calls all listeners that is subscribing the store to notify.

```javascript
var valueEl = document.getElementById('value')

function render() {
  valueEl.innerHTML = store.getState().toString()
}

render()
store.subscribe(render)
```

- In this case, we have `render()` function, it is called back and update the DOM element with the initial value.
- Now in the browser, we will se the number `0` shown.

### Updating state when action is sent


```javascript
document.getElementById('increment')
	.addEventListener('click', function () {
	  store.dispatch({ type: 'INCREMENT' })
	})
```
- When users click on the button "+", store dispatches the action with type `'INCREMENT'` to the reducer of the store and the flow is the same as explanation above.
- Function `currentReducer` is called with state is `0` and action's type is `'INCREMENT'`.
- Because `'INCREMENT'` is a case inside `counterReducer` function, so the new state now is equal to `0 + 1` and returned to the state of the store.
- Next, again it notifies listeners to let them know state is updated successfully.
- Now, in the screen we will see Clicked: 1 times
- The flow is similar to other action types

So this is basically how Redux works under the hood. In real life project, Redux store may have multiple `reducers` and `midleware`, and 3rd-party libraries enhance Redux workflow. But at very its core that's how it works basically !