Now that we got that simple encapsulation mechanism covered, I would like us to move to the core of this article — the hooks. Right off the bat I’d like to introduce you to a new concept:

The hooks queue

Behind the scenes, hooks are represented as nodes which are linked together in their calling order. They’re represented like so because hooks are not simply created and then left alone. They have a mechanism which allows them to be what they are. A hook has several properties which I would like you to bare in mind before diving into its implementation:

Its initial state is created in the initial render.

Its state can be updated on the fly.

React would remember the hook’s state in future renders.

React would provide you with the right state based on the calling order.

React would know which fiber does this hook belong to.

Accordingly, we need to rethink the way we view the a component’s state. So far we have thought about it as if it’s a plain object:

React state — the old way.

But when dealing with hooks it should be viewed as a queue, where each node represents a single model of the state:

React state — the new way.

The schema of a single hook node can be viewed in the implementation. You’ll see that the hook has some additional properties, but the key for understanding how hooks work lies within memoizedState and next . The rest of the properties are used specifically by the useReducer() hook to cache dispatched actions and base states so the reduction process can be repeated as a fallback in various cases:

baseState - The state object that would be given to the reducer.

- The state object that would be given to the reducer. baseUpdate - The most recent dispatched action that created the baseState .

- The most recent dispatched action that created the . queue - A queue of dispatched actions, waiting to go through the reducer.

Unfortunately I haven’t managed to get a good grasp around the reducer hook because I didn’t manage to reproduce almost any of its edge cases, so I wouldn’t feel comfortable to elaborate. I will only say that the reducer implementation is so inconsistent that even one of the comments in the implementation itself states that “(it’s) not sure if these are the desired semantics”; so how am I supposed to be sure?!

So back to hooks, before each and every function Component invocation, a function named prepareHooks() is gonna be called, where the current fiber and its first hook node in the hooks queue are gonna be stored in global variables. This way, any time we call a hook function ( useXXX() ) it would know in which context to run.

Hooks queue implementation in a nutshell.

Once an update has finished, a function named finishHooks() will be called, where a reference for the first node in the hooks queue will be stored on the rendered fiber in the memoizedState property. This means that the hooks queue and their state can be addressed externally: