36 ms·
> impossible with class components I'm not sure thats really true. In a different world, the react constructor functions would have access to `this.useState` a
by spion 4y ago
> impossible with class components
I'm not sure thats really true. In a different world, the react constructor functions would have access to `this.useState` and `this.useEffect` and then you could extract any logic you like by dispatching `yourFunction(this)`. HOC are not the only alternative design.
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.
- codeptualize 4y agoAccess to this.setState would be horrible, imagine the conflicts. You need separate state, and then you sort of have hooks again.
- spion 4y agosee https://news.ycombinator.com/item?id=35192312 https://news.ycombinator.com/item?id=35192312
- rimunroe 4y ago> 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.
- spion 4y agoIt really IS weird to reason about. `useEffect(f, [])` runs on every render, but only installs the effect once. The other times its not installed. That means standard language facilities such as *closure capture* don't quite work as expected unless you pass them in that list `[]` too Its not impossible to get used to it, but thinking in hooks is profoundly different than thinking with normal javascript. I think the original design wanted so badly for state to fully live outside of React, but unfortunately, the information about what components are currently active on the screen is very relevant for what the state should be doing. As such, I think that approach just doesn't work no matter how much we'd like it to work.
- rimunroe 4y agoI never said they weren't weird to reason about. Side effects are hard to reason about. I was responding to the claim that hooks "push the boundaries of the language". Closures work exactly the same as they always do, but they're also something a lot of people struggle with and always have. This is all entirely normal JavaScript, but that doesn't mean it's going to be easy for people. Class components were incredibly easy to introduce subtle bugs in, especially because of the abilities to do things like define your own non-idempotent constructor or doing things like assigning to and reading from instance properties (refs are like this too, but I've seen much jankier code around class instances than the--already considerable amount--I've seen with refs). Hooks make you run into those potential bugs head-first, and that's always seemed to me like what a large part of the "hooks are hard to reason about" complaints stem from. The other parts are people who expect functions to be invoked in a different way than they are. I don't really know what to tell those people beyond asking them if they've ever seen code calling a component directly.
- spion 4y ago> Closures work exactly the same as they always do, but they're also something a lot of people struggle with and always have. Its not normal for a render function to run all the time but the closures mentioned in it to be completely ignored. Thats completely the opposite of how most other APIs work in the language. Usually, a (constructor) function runs once, and the closures returned by it may get run multiple times. Completely ignoring the passed closures to hooks in many/most cases is something very specific to React.