Yet another OOP/C# person (me) trying to understand the mechanics behind React Hooks
Tags: javascript, react, hooks
Published: 2020-09-30 12:13:59

I've written this article as a memo to my future self as well, and my goal was to make it short. If there is something here that isn't technically correct, a feedback would be appreciated.

### What is the magic behind the simplicity of React Hooks?

Coming to React from an OOP/MVVM/C# background, for a while I was having this "how-does-it-work-behind-the-scence" syndrome about React hooks. 

As they get called from what seemingly is a *plain, stateless JavaScript function*, and yet hooks maintain their state.

Particularly, about how multiple hooks of *the same kind* coexist within *the same function component* and persist their state across *multiple renders*.

For example, across multiple invocations of the following `MyComponent` function ([try it in the CodePen](https://codepen.io/noseratio/pen/PoNvaav)):

```JS
function MyComponent() {
  const refUp = useRef(0);
  const refDown = useRef(0);

  const [countUp, setCountUp] = useState(0);
  const [countDown, setCountDown] = useState(0);

  const clicked = () => {
    setCountUp(count => count + 1);    
    setCountDown(count => count - 1);    
  };

  console.log("rendering");
  
  return (
    <p>
      <span>Up: {refUp.current++}</span><br/>
      <span>Down: {refDown.current--}</span><br/>
      <span>Counts: {countUp}, {countDown}</span><br/>
      <button onClick={clicked}>Count</button>
    </p>
  );
}

```

How is it possible that *`refA.current` and `refB.current` can be mutated and still survive multiple renders, keeping their values*, without relying upon something like JavaScript's `this`? 

Especially, given they both were created with two identical invocations of `useRef(0)`? My guts were telling me there should be a unique name parameter, like `useRef(0, "refA")`, but there isn't.

The same question applies to `countUp`, `countDown` and the corresponding `useState(0)` calls which initialize these variables.

### Something has got to maintain the state for us.

And there has to be some kind of 1:1 mapping for each hook into that state. 

As it turns, there is no magic. In a nutshell, here is my understanding of how it goes:

- First of all, hook calls don't work outside React function components, by design. They implicitly rely upon the calling context React provides them with, when it renders the component.

- React maintains its own internal state for the life-time of the web page. While not exactly accurate, let's called it React's static state.

- Each component like `MyComponent` above has a dedicated entry in React's static state, and that entry keeps the state of each hook used by the component between renders.

- When a hook like `useRef` is called, React knows which component is calling it (the one currently being rendered), so React can retrieve that individual component's state entry it have previously mapped and stored in its static state. That's where the current values of hooks like `useRef` and `useState` are stored, per component. 

- Initially, such entry gets created and mapped when the component gets mounted (or perhaps upon the first render, I didn't dig deep into that, but it's done once).

- The exact order of calls like `useRef` or `useState` within the component function matters, and it should remain the same across subsequent renders. In our case, React initially creates two entries for `useRef` in its internal state for `MyComponent`, then two entries for `useState`. 

- Upon subsequent renders (invocations of `MyComponent`), React knows how to access the correct state and which values to return, by the order of each `useRef` or `useState` call.

- I'm not sure about exact data structure used by React to map hooks by the order of their appearance in the function component, I didn't dig into that either. But it's easy to think about the order of each hook call as of an index in the array of hooks maintained by React for the life cycle of our component.

- Thus, if we mess about this order across multiple renders, our state will be broken, because the original indexing wouldn't be correct. E.g., the following made-up example will likely screw up the state of `refUp` and `refDown` very soon, because their order of `useRef` calls is inconsistent: <br>

    ```JS

     // don't mess up the order of hooks like this:
     let refUp;
     let refDown;
     if (Date.now() & 1) {
       refUp = useRef(0);
       refDown = useRef(0);
     } 
     else {
       refDown = useRef(0);
       refUp = useRef(0);
     }

    ```
Finally, *hooks are not available for class components*. While in theory it might have been possible to support hooks for class components' `render()` method, it's React's philosophy to keep the state in the class `this.state` and use `this.setState()` to update it, for class components.

The following resources greatly helped me to understand these hook mechanics: 

- [ReactHooks.js] (https://github.com/facebook/react/blob/master/packages/react/src/ReactHooks.js)
- [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html)
- [Hooks FAQ - Under the Hood](https://reactjs.org/docs/hooks-faq.html#under-the-hood)
- [Deconstructing the Magic Behind React Hooks](https://hackernoon.com/deconstructing-the-magic-behind-react-hooks-33ca987e5307)
- [Under the hood of React’s hooks system](https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9dba)
- [Why Do React Hooks Rely on Call Order?](https://overreacted.io/why-do-hooks-rely-on-call-order/)
- [Making Sense of React Hooks](https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889)