DEV Community

Cover image for How Redux works ? (Only HTML & Pure JS)
Paper Coding
Paper Coding

Posted on • Edited on

9 2

How Redux works ? (Only HTML & Pure JS)

This is a code example of Redux with only HTML & pure JavaScript. Code sandbox



<!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>


Enter fullscreen mode Exit fullscreen mode

The webpage looks like this
Alt Text

  1. createStore & counterReducer


// 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);


Enter fullscreen mode Exit fullscreen mode
  • 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

Here is simplified version of createStore in redux source code:



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 };
}


Enter fullscreen mode Exit fullscreen mode
  • 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 ?



// 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
}


Enter fullscreen mode Exit fullscreen mode
  • 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.


var valueEl = document.getElementById('value')

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

render()
store.subscribe(render)


Enter fullscreen mode Exit fullscreen mode
  • 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



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


Enter fullscreen mode Exit fullscreen mode
  • 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 !

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (3)

Collapse
 
ahmetcengiz06 profile image
ahmetcengiz06

I use index.js file that contains in script tags codes. However, getting error Redux.create is not a function.

Collapse
 
papercoding22 profile image
Paper Coding
Collapse
 
ahmetcengiz06 profile image
ahmetcengiz06

I want to create a another js file for script tag but getting error. I don't want to any script code in index.html but unpkg.com/redux@latest/dist/redux.... isn't reached other js file

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Dive into this thoughtful article, cherished within the supportive DEV Community. Coders of every background are encouraged to share and grow our collective expertise.

A genuine "thank you" can brighten someone’s day—drop your appreciation in the comments below!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found value here? A quick thank you to the author makes a big difference.

Okay