Is there an explanation somewhere on react.dev covering how Hooks actually work under the hood? They're the most magical part of React, and everybody I know (including me) had a hard time actually grasping how they work and why, for example, you can't use a hook inside an if branch.
Edit: there are a lot of good--and varied--explanations here. Which is why I think the docs should cover it in-depth. It's confusing.
However, _conceptually_ I'd recommend to think of Hook return values similar to "extra inputs" to your function, kind of like extra arguments. There are different ways to formalize it in different languages. We picked plain function calls for simplicity and low overhead, although you could imagine `yield` with generators or something like that.
My high level understanding of how they work is that they remember the sequence in which they were called - so if you call the same hook three times, those three calls are held in a list - and future actions that should retrieve them get access to the correct data.
If you were to call them in an "if" branch it would mess up that queue mechanism.
I read the code when they added them. It may have changed, but here it is:
1) A hook adds a function or variable to one of several lists attached to the component object, when it's instantiated (yes, even your "functional" components are objects, and I don't just mean in the JS-functions-are-actually-objects sense—at least, they were when I read it)
2) Subsequent calls either call those functions, or accesses/modifies the value, in a FIFO manner, reading them out of those lists. This is why you can't mess with hook ordering in e.g. loops.
It's basically just methods and properties on an object, but with FIFO access based on declaration order, instead of using a lookup table of some sort.
[EDIT] A poster correctly pointed out (then deleted their post) that I wrote "loops" where I meant "conditionals". Technically sorta-true (though not quite, as phrased) if the loop isn't the same length every time, but yeah, I meant conditionals. Point is, the reason order matters is that the whole thing's just a bunch of FIFO queues, more or less.
Basically the way of thinking about is that there is a runtime that knows what component is rendering, and your hooks are communicating with that global runtime. This is why hook order and consistency matters - there is basically something globally that identifies a hook by its index order of execution and the identity of the component instance that is currently rendering.
So there is a data structure that store says `[useMemo, useState, useEffeect]` - and when you component re-renders, is unmounting, or has effects to trigger it uses the index order to lookup the bit of state that needed to persist.
1. They work by setting a global variable to the value of the current component then calling the render function. Whenever you call a hook you're effectivelly dispatching it to the component in question, OOP style.
2. React counts the number of executions of (certain) hooks. This count is how it knows which state to get from the store as a return value from `useState`. useState is effectively `getStateAndSetter()` but it doesn't pass a key name of any kind, so the implicitly passed key is `hookCount++`. This is why you can't call hooks conditionally, or state would get all messed up - if a condition turns false and one hook doesn't run that render, all getStateAndSetter calls that run after it will be off by one.
Here's how I'm pretty sure they work (although I haven't actually looked at the internals). Since your `<Tagname props>children</Tagname>` gets turned into `React.createElement(Tagname, props, children)`, this means your `Tagname` function isn't called directly. So before it calls that function, it sets up a "context" that the hooks will use during that function call (with a global variable pointing to that context so the hooks can find it). We could use an array with one element for each hook invocation. So each useState would use a different slot in that array, etc. This is also why the order and number of hooks must always be the same for a given function, since their identifier is their index in that array.
Additionally, this context would also have the context of children elements, so things can actually be persisted across function calls and React can know what needs to be mounted & unmounted.
Also note that because Tagname isn't called directly, it's also how React is able to do its diff and only actually call what is needed.
This is also why if you're generating a dynamic number of elements (ie outputting an array of elements), you should provide a `key` prop, so it can link up the elements to their corresponding past states each time the function runs.
Everytime I try to understand how something in react works I actually look into how it's implemented in preact. Hooks is around 500 LOC. https://github.com/preactjs/preact/blob/master/hooks/src/ind...
React does basically the same, just in a more complex way (because fiber and native etc). But just giving you a mind model the preact implementation is enough.
Edit: there are a lot of good--and varied--explanations here. Which is why I think the docs should cover it in-depth. It's confusing.