How redux works?

Redux is a predictable state container for javascript applications. In layman's term, We store our data in a centralized place and pass it where ever needed by the application.

Few points about redux:

  1. Single source of truth - A plain single object to store your application data.
  2. Predictable - Behaves consistently no matter the environments (client, server and native).
  3. Developer Friendly - With redux devTools it is easy to debug and test our application.
  4. Flexible - Works with any UI layered application.

Below are the core concept of redux:

Skeleton code:
function createStore() {
  // To get the state
  function getState() {}

  // To dispatch or send an action
  function dispatch() {}

  // To subscribe to the state change
  function subscribe() {}

  // Return the above methods
  return { getState, dispatch, subscribe };
}

// To combine all the reducers
function combineReducers() {}

Step 1: createStore()

To create our data store of the application. The main concept of redux is we can have only one store. createStore() method accepts three arguments as following.

  1. Reducer (Root reducer)
  2. Preloaded state (initial state - optional)
  3. Enhancer (optional) - Just to reduce the scope of this post we will not look into this.

createStore() function will return 3 methods such as getState, dispatch, subscribe.

Step 2: getState()

This method has one job which is always return the updated state from the store.

function createStore(reducer, preloadedState, enhancers) {
  // If preloaded state else empty object
  let state = preloadedState || {};

  // Return the store state
  function getState() {
    return state;
  }

  return {
    getState,
  };
}

Step 3: subscribe()

subscribe() method is useful when we want to listen to whenever the store state is updated. This method also returns unsubscribe() method, so that we can opt-out when we no longer need it.

function createStore(reducer, preloadedState = {}, enhancers) {
  // If preloaded state else empty object
  let state = preloadedState || {};

  // To store the list of subscribers for the state change
  let listeners = [];

  // Return the store state
  function getState() {
    return state;
  }

  // To subscribe to state change
  function subscribe(listener) {
    listeners.push(listener);

    // Returns an anonymous function to unsubscribe it
    return function () {
      listeners = listeners.filter((l) => l !== listener);
    };
  }

  return { getState, subscribe };
}

Step 4: dispatch()

Dispatches an action that contains information about the change. The information is plan object which contains a type and optional payload. The dispatch() method is the only way to update the state in the redux store.

dispatch() method does 2 things:

  1. Update the state using the type in the reducer passed to createStore() when we created the store.
  2. Calls all the subscribers if present, after the state has been updated.
function createStore(reducer, preloadedState = {}, enhancers) {
  // If preloaded state else empty object
  let state = preloadedState || {};

  // To store the list of subscribers for the state change
  let listeners = [];

  // Return the store state
  function getState() {
    return state;
  }

  // To subscribe to state change
  function subscribe(listener) {
    listeners.push(listener);

    // Returns an anonymous function to unsubscribe
    return function () {
      listeners = listeners.filter((l) => l !== listener);
    };
  }

  function dispatch(action) {
    state = reducer(state, action); // save the updated state from reducer

    // call all the listeners
    listeners.forEach((listener) => listener());
  }

  // To initializing the store with initial state of the reducers
  dispatch({ type: '@@redux/INIT' }); // now you know why you see this log on page load

  return { getState, subscribe, dispatch };
}

Step 5: combinerReducers()

Combine reducers function is responsible for combining all reducers and call it to update the state, when an action is dispatched.

combinerReducers function does the following:

function combineReducers(reducers) {
  return (state = {}, action) => {
    return Object.keys(reducers).reduce((nextState, key) => {
      nextState[key] = reducers[key](state[key], action); // Calling the reducers
      return nextState;
    }, {});
  };
}

Final thoughts

I have learned so much about redux by writing this post and I hope you have learned something new as well. The code I wrote is just to show how redux would have been implemented. And this code is only for learning purposes. Not meant to be used in production or development.

Thanks for reading to the end. If you have any doubts post your comments below. See ya in the next post.

Join 300+ Readers

Sent out once every month. No spam 🤞