> Hooks really push at the boundary of the language and I'm not sure they're an optimal path once you actually start dealing with state.
How do they push at the boundary? I feel like so many people talk about them being magical, but I don't see how they're any different than any other effectful function call. Is the idea of tracking call order that exotic?
This is a very simplified example of how hooks work under the hood:
let hookStates = [];
let currentHookId = 0;
let firstRun = true;
function endRun() {
firstRun = false;
currentHookId = 0;
}
function useState(initialValue) {
const hookId = currentHookId++;
if (firstRun) {
hookStates[hookId] = initialValue;
}
function setter(nextState) {
hookStates[hookId] = nextState;
}
return [hookStates[hookId], setter]
}
let actions;
function run() {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
// this is done because we're not setting up event handling
// and we need some way to trigger external updates
actions = {setA, setB}
endRun();
return a + b;
}
function assert(received, expected) {
if (received !== expected) {
let error = new Error(`Expected ${expected}, but received ${received}`);
error.name = 'AssertionError'
throw error;
}
}
assert(run(), 3);
actions.setA(3);
assert(run(), 5);
actions.setB(5);
assert(run(), 8);
actions.setA(1)
actions.setB(-1)
assert(run(), 0);
Obviously this example doesn't include tracking more than one component, but as far as I know hook state gets tracked pretty much the same as the state on class component instances.
I could understand thinking they're magical if you were under the impression you were the one calling the component functions rather than React, but I think my impression was always that it was still React's job to call the function component (otherwise, createElement seems like a waste). They might have at one point said that it was okay to do that in tests, but I'm not sure about that, and it would have been out the door the moment function components started supporting refs.