function MyComponent({}) {
const [count, setCount] = useState(0);
const [active, setActive] = useState(true);
useEffect(() => if (active) setTimeout(1000, () => setCount(count+1)), [count, active]);
...
can kind of be thought of as if it accepts an extra hidden set of parameters, kind of like this: function MyComponent({}, hook1 : useState, hook2 : useState, hook3 : useEffect) {
const [count, setCount, hook1Result] = hook1(0);
const [active, setActive, hook2Result] = hook2(true);
const hook3Result = hook3(() => if (active) setTimeout(1000, () => setCount(count+1)), [count, active]);
...
return [<div></div>, [hook1Result, hook2Result, hook3Result]];
}
In this version, those hook functions are side-effect free, pure functions that our caller provides us with, closed over the previous stored state of our component in the tree; and when we return the results along with our rendered output, our caller has the new state of our component together with our desired DOM.Instead of forcing us to write all that extra code, route those extra variables, and figure out the metadata mechanism whereby the caller of the render method figures out what hook functions to supply, the rules of hooks let you use JavaScript's imperative, procedural syntax to call hooks which are implemented using ugly side-effecting impure techniques, but in a way that pulls off something semantically identical to this pure functional flow.
It's a pretty neat trick, honestly.