How Does "prevState" Works Under the Hood

Tags: javascript, react, webdev
Published: 2022-02-22 22:02:45

---
canonical_url: https://ogzhanolguncu.com/blog/how-does-prevstate-works-under-the-hood
---

We have always been told to use **prevState** when dealing with **useState** but not really why we need it in the first place. Today, we will deep dive and see how it works under the hood to retrieve the latest state without the need of render cycle - render cycle refers to VDOM updates, not actual browser refresh. But before going forward, first, we need to see how the real problem occurs when the **state** is used instead of **prevState**.


```javascript
const [counter, setCounter] = useState(0);
  return (
    <div className="App">
      <div>Counter: {counter}</div>
      <button
        onClick={() => {
          setCounter(counter + 1);
          setCounter(counter + 2);
          setCounter(counter + 3);
        }}
      >
        Click me to increase the counter!
      </button>
    </div>

```

In reality, this should increase the **Counter** by six each time we click, but it's only taking the last one to account.
So what's the reasoning behind this? Is this working incorrectly, or is this the intended behaviour? It turns out it's not falsy or incorrect; it is working as expected programmatically, at least.
Because for React to access the `counter` state, it should complete its rendering cycle. But, since we force React to read the `counter` state before the cycle completion, it's only referring to the last one.

Okay, let's see how it behaves when we introduce **prevState**.

```javascript
const [counter, setCounter] = useState(0);
  return (
    <div className="App">
      <div>Counter: {counter}</div>
      <button
        onClick={() => {
          setCounter(prevState => prevState + 1);
          setCounter(prevState => prevState + 2);
          setCounter(prevState => prevState + 3);
        }}
      >
        Click me to increase the counter!
      </button>
    </div>

```

Now it's working as we expected. But, how? To answer this question, we'll build a simple React clone and see how it internally manages **prevState**.

React used to rely on `this` in class-based components, but now it's using closures under the hood to manage hooks states. Pretty much all of the hooks use closures to access information about previous renders.

A little recap for `closures` to not get lost in the following examples.

## Closures

Consider the following code:

```javascript
const add = () => {
  let counter = 0;
  return (x = 1) => {
    counter += x;
    return counter;
  };
};

const foo = add();

foo(5); // 5
foo(5); // 10
```

Closure functions always hold a reference to an inner variable to keep track of it. The inner function is only accessible within the function body, and this inner function can access `counter` at any time. So between function calls `counter` variable will always point to the latest variable state.

In the example above, if we go ahead and use a regular function, we would end up with **5** twice, but since we keep track of value inside function thanks to closure, we keep adding to the accumulated value.

---

Now, going back to our original example. We will build a simple React clone that utilizes closures under the hood to persist states between renders.

```javascript
function Counter() {
  const [count, setCount] = React.useState(5);

  return {
    click: () => setCount(count + 1),
    _render: () => console.log('_render:', { count }),
  };
}
```

At first glance, you are probably saying we need an object with two functions, one to take care of **useState** and another one for our pseudo rendering. And definitely, a variable to persist
the state.

```javascript
const MyReact = () => {
  let val = null;

  return {
    render(Component) {
      const Comp = Component();
      Comp._render();
      return Comp;
    },
    useState(initialValue) {
      val = val || initialValue;
      const setState = (nextState) => (val = nextState);
      return [val, setState];
    },
  };
};
```

Let's start with `render()`. The `render()` function accepts a component, and all it does is invoke the `_render()` and return the component for future use because we need to keep its reference. Without return **Comp**, we can invoke neither `click` nor `_render` because it's this function that carries the details about our component.

The `useState()` is pretty straight forward. It takes the default value and assigns it to `val`, but only `val` is not present. Then, we have `setState()` to assign new values to our state.
Finally, we return a tuple - array with 2 elements.

```javascript
const MyReact = () => {
  let _val = null;

  return {
    render(Component) {
      const Comp = Component();
      Comp._render();
      return Comp;
    },
    useState(initialValue) {
      _val = _val || initialValue;
      const setState = (nextState) => (_val = nextState);
      return [_val, setState];
    },
  };
};

const React = MyReact();
function Counter() {
  const [count, setCount] = React.useState(5);

  return {
    click: () => setCount(count + 1),
    _render: () => console.log('_render:', { count }),
  };
}

let App;
App = React.render(Counter); // _render: {count: 5}
App.click();
App.click();
App.click();
App = React.render(Counter); // _render: {count: 6}
```

Now, if we run this piece of code, it only prints twice because we called render twice - that's pretty expected. But, we clicked three times; why did it print count **6** instead of **8**.
Similar to real React our **MyReact** is waiting for React to render. Without render, it cannot process the upcoming state updates. Therefore relies on render.

```javascript
let App;
App = React.render(Counter); // _render: {count: 5}
App.click();
App = React.render(Counter); // _render: {count: 6}
App.click();
App = React.render(Counter); // _render: {count: 7}
App.click();
App = React.render(Counter); // _render: {count: 8}
```

If we let it render, then it prints correctly.

So, how can we access the `_val` inside **MyReact**? You guessed it right, we need to give a callback to `setCount` and change the `useState` a bit. And, if you are worried about callback, don't, because it's something we already know and use.

```javascript
useState(initialValue) {
      _val = _val || initialValue;
      const setState = (nextState) => {
        _val = typeof nextState === "function" ? nextState(_val) : nextState // Changed this line to accept callbacks
      }
      return [_val, setState];
}
```

```javascript
const React = MyReact();
function Counter() {
  const [count, setCount] = React.useState(5);

  return {
    click: () => setCount((prevState) => prevState + 1), // Sending callback to access closure
    _render: () => console.log('_render:', { count }),
  };
}
```

In `setCount` all we do is giving an arrow function that accepts a variable and adds 1 to it.

```javascript
setCount((prevState) => prevState + 1);

const setState = (incVal) => {
  _val = typeof incVal === 'function' ? incVal(_val) : incVal;
};
```

We no longer need to rely on render cycles, we can directly access the state closure via prevState.

```javascript
let App;
App = React.render(Counter); // _render: {count: 5}
App.click();
App = React.render(Counter); // _render: {count: 6}
App.click();
App = React.render(Counter); // _render: {count: 7}
App.click();
App.click();
App.click();
App = React.render(Counter); // _render: {count: 10}
```

By the way, this does not mean we need render anymore. Whether you like it or not React keeps rendering, but we can always get **fresh** states during rendering phase instead of **stales** one.

### Wrapping Up

Some of the concepts above might seem vague, but in due time with lots of pratice they start become more understandable.

Important takeaways:

- Functional components use **closures** under the hood to store states.
- Always rely on **prevState** to avoid stale states.
- Learning the core concepts of the language will always help to get deeper understanding