3 ms·
I'll try to fix the comment you're replying to: I think of React components as not closures but coroutines (and hooks are its yield points). Hooks are implici
by kaoD 2y ago
I'll try to fix the comment you're replying to:
I think of React components as not closures but coroutines (and hooks are its yield points).
Hooks are implicitly keyed by index which is the most magic-y/surprising part... but I'm sure if you had to manually key them that'd be criticized too (and would be abused to death-by-bugs, so I can see why they went with this design).
If you understand both points above, you understand hooks.
I still fail to get the (usual) criticism of hooks. They have lots of warts but the API (which is often what gets mentioned) is the most superficial and less annoying criticism. Feels like something that would be mentioned after just skimming the docs. Very shallow.
On the contrary: useEffect is a footgun and deserves criticism. useRef being overloaded to mimic instance variables is confusing for newbies (but this is just a naming issue IMO). The difference between normal/layout/insertion effects is complex and subtle. The new stuff that tries to solve some issues with the concurrent mode (like transitions/deferred value/etc.) feels like a huge hack.
React 19 will come with its own warts (actions, RSC...)
But the API? I don't care at all. Pretty simple, at least for my mental model.
- com2kid 2y agoThe complaint is they prior to hooks react didn't have any of this complexity. It was pretty simple to understand. Class components works mostly as you expected them to. There are a handful of things that were really hard to do with class components that hooks + function components made easier, but lots of other things became more complicated with each new set of features react has added since then. At this point the solution to "class components are complicate" is far more complicated than class components ever were.
- kaoD 2y agoI understand the complaint but my point is that, in my experience (which admittedly might be biased), the complaint usually does not resonate with anyone that did actually use React for at least a moderately complex app. Hook's API is not perfect but it's a good-enough abstraction that allows the user to have even better abstractions and separation of concerns. Actual React users did not care about that because the pragmatism far outweighs the theoretical ugliness... which honestly is not even that ugly if you have a mental model similar to coroutines (of course if all you do is OOP a class will look better to you...) I have recently been fixing some stuff in my old React pre-hooks code and I hated it because class-based components had all sorts of concerns intermixed on their lifecycle methods... no matter how much you tried to abstract them. Abstracting those into reusable hooks was a breeze and made everything much easier to follow and maintain. Hooks are far better from a pragmatic point of view. > There are a handful of things that were really hard to do with class components that hooks + function components made easier, but lots of other things became more complicated Like what? Does not match my experience at all.
- watwut 2y ago> of course if all you do is OOP a class will look better to you... Funny thing is, I do not know what coroutines are and have zero experience with them. I am object oriented class programmer who never cared about functional programming. I figured hooks intuitively and straightforwardly when I had to work with react a year ago. Ok, function with state, but it was easy to read them, imagine what they do and maintain the code. It just seemed as an improvement over the old react to me, despite having no coroutines knowledge.
- kaoD 2y agoExactly! To be honest they're not full-blown coroutines (since the React runtime cannot control when the component progresses) which might be a bit more complex to grasp... but the idea of "yielding control" to someone else (i.e. to React, when calling one of its hooks) is there and as you say it's pretty straightforward.
- kccqzy 2y ago> because class-based components had all sorts of concerns intermixed on their lifecycle methods I think this just means your components are too large and try to do too many things. When your components are simple, their concerns are simple enough that they can immediately be understood, even if spread over several lifecycle methods.
- recursive 2y agoFor me, it's not OOP vs functional. I find "functional" to be a misnomer as its applied to most react components. Functional used to have a clear definition about having a stable return value and only varying based on parameters. However, the same problem started afflicting class-based components prior to the introduction of hooks. When fibers were created, component instances no longer tracked their own state. State was injected into them prior to invoking the render function, based on reconciliation. I suppose even before that the reconciler was still choosing which component instance to render. But at least components had an identity that could be addressed in application code. Now component identity is effectively the fiber instance identity (or its alternate), which is impossible to get a reference to. In real life, people really seem to like hooks. I can see some of the benefits they provide over the OOP class-based component model. But I can't avoid also seeing the mental foot-guns associated with it. The very first time I every tried to write a react app, I got tripped up by this. I add a component that moves around in a parent component conditionally. So I had `const inner = <Inner />`, and then based on some condition it would be inserted in various places in the rendered parent component. Of course after much wailing and gnashing of teeth, I learned that this variable declaration wasn't establishing a component identity. It merely creates an "element", even if it has a key attribute. Component identity, as used to retrieve state, is fundamentally tangled up with reconciliation which can only see the final rendered output of a component. No other components can have state, at least as recorded by a hook. Most people don't seem to have trouble with this, but it's not how I naturally think of things.
- recursive 2y agoWhich coroutine gets resumed by a rendering component is determined by reconciliation. Rendered components themselves have no identity aside from the reconciler's algorithm for equality. For me, this is the root of the problem. Or one of them anyway. Hooks are keyed by execution order, but also the reconciler's opinion about component identity, which you can only control indirectly. Fortunately it usually does what you want. Unfortunately it's not all the time. I feel like I do understand hooks, but it's not from reading the docs, and it's not from using them. I know this is not typical but doing those things didn't seem to illuminate much to me. I would still find them doing things I didn't want that I couldn't explain. It was only after reading the source that I feel like I understand the model. And personally, it's not one I would use by choice. I can do it if I'm required by a team I'm on. But for me, there's a mental overhead for "thinking in react". It's not a natural set of constraints for me.
- afiori 2y agoI think that the clearer way to explain hooks is just referencing how they are implemented. When a function component is called it is called on a "fiber" a stateful representation of the component instance. This fiber is available to the hook as if it was global variable that is set before the component is rendered. This is why you cannot call a hook is a setTimeout or a callback to another component: the global fiber is either unset or has a different value. The other part is that each hook invocation works on a state accessed as sorta fiber.hooks[index++]. So for example you can call hooks in a loop or in conditionals or in synchronous callbacks, but each rendering must be compatible with the first. Eg let s; If(Math.random()<0.5) s=useState({}); else s=useState([]) Should work. You could also do the same with useEffect if you keep the length/nullness of the dept array.
- 8note 2y agoIve probably been doing it wrong, but isn't useEffect needed to make any hooks work? useState does nothing most of the time
- kaoD 2y agohttps://react.dev/learn/you-might-not-need-an-effect https://react.dev/learn/you-might-not-need-an-effect
- recursive 2y agoNo. `useState` returns the value of the state, and a setter. It's not about side effects, it's about the value. The returned setter triggers a re-render. That's not nothing.