Building React in Vanilla JS : 103 - Adding State

Yogesh KanwadeYogesh Kanwade
2 min read

Let’s build our useState hook!

So to mimic the useState hook from React.js, the first thing that comes to mind is to define a function that takes in the initial value and returns the initial value along with a function to update that value. During this we need a data structure to store the state value. Let’s go with an array. Our _state array. To keep a track of state variables we will have an index _stateIndex.

So firstly we check and initialise the value in our _state array. Then comes in the setter function which closes over the currentIndex.

  • Each time we call useState, it captures the value of currentIndex.

  • That value is remembered by the specific setState function it returns.

  • When setState is later called (e.g. on button click), it uses the correct slot in the _state[] array because it closed over the value of currentIndex when it was created.

After updating our state we need to trigger a re-render to reflect the updated values. For this let’s define a simple rerender function.

function rerender() {
  _stateIndex = 0;
  render(App(), document.getElementById("app"));
}
💡
useState relies on the order in which it is called. At every rerender we must start from _stateIndex zero to correctly pull the related value from _state array. Thus set the _stateIndex to zero inside our rerender() function.

Now here’s our own useState hook:

const _state = [];
let _stateIndex = 0;

const useState = (initialValue) => {
    const currentIndex = _stateIndex;

    if (_state[currentIndex] === undefined) {
        _state[currentIndex] = initialValue;
    }

    const setterFuntion = (newValue) => {
        _state[currentIndex] = newValue;
        _stateIndex = 0;
        rerender();
    };

    const value = _state[currentIndex];
    _stateIndex++;

    return [value, setterFunction];
};

Limitations

Our _state and _stateIndex are globally scoped. If two different components, say ComponentA and ComponentB, are both using useState, their states will collide in the single _state array. _stateIndex will continue incrementing across all component renders, leading to incorrect state retrieval.

Next Step

The next step would be to figure out how to associate _state and _stateIndex with individual component instances and then how to trigger a re-render of only the affected component (the latter can be tackled once we finish with our component-scoped state).

Code Sandbox

1
Subscribe to my newsletter

Read articles from Yogesh Kanwade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Yogesh Kanwade
Yogesh Kanwade

I'm Yogesh Kanwade, a final year Computer Engineering student with a deep passion for software development. I am a continuous learner, with hardworking and goal-driven mindset and strong leadership capabilities. I am actively exploring the vast possibilities of Web Development along with AWS and DevOps, fascinated by their impact on scalable and efficient web solutions.