by vicarrion on 4/27/20, 2:06 PM with 290 comments
by desc on 4/27/20, 10:43 PM
React Hooks are a fucking stupid idea and always were.
They're basically just adding dynamic scoping to a language and framework which doesn't need it, in one of the most 'magical' and confusing ways possible. You have to care about execution order to understand exactly how they'll all work and that will bite you eventually if you're building anything without knowledge of all the contexts in which it's called.
There's a reason that most languages stick to lexical scoping: you can see the dependencies, in the same file.
And a large portion of the value of functional languages is that they avoid state, making magic-at-a-distance impossible.
Boilerplate is not the problem. Magic is the problem.
by dcre on 4/27/20, 3:34 PM
The problem with the JS class representation is that people already understand what classes and instances are, and that leads to incorrect inferences about how React is working. In addition to better-organized code, the hooks abstraction is partly aimed at preventing people from making those wrong inferences. This also explains why they are uncomfortable compared to classes and functions — the point is that was a false comfort because those representations are misleading.
Dan Abramov calls hooks a "missing primitive":
"Why are these models insufficient to describe React? “Pure function” model doesn’t describe local state which is an essential React feature. “Class” model doesn’t explain pure-ish render, disawoving inheritance, lack of direct instantiation, and “receiving” props.
What is a component? Why do you start writing it one way and then have to convert into another way? Why is it “like A but also like B”? Because it’s neither. It’s a thing of its own. A stateful function with effects. Your language just doesn’t have a primitive to express it.
That’s what Hooks are. Those missing primitives. They are library-level but conceptually they are part of the “React language”. Hence the language-like “rules”.
They could be syntax. They would be in Eff or Koka. But the benefits are not worth the friction it creates in JS."
https://twitter.com/dan_abramov/status/1093696560280596491
by lucideer on 4/27/20, 3:11 PM
However, I'm disappointed.
In reverse order:
> 5. They Complicate Control Flow
A set of contrived examples, within which the only genuinely confusing part is not related to React at all. It's Javascript's object non-equality ({ multiplier: 5 } !== { multiplier: 5 })
> 4. The Rules of Hooks Limit Your Design
This is an interesting section but seems to point to a pattern that would lead to serious issues given any library/paradigm. It's criticising the Rules for their inflexibility, but in this instance they're helping you by pointing out the pitfalls of your approach, and encouraging you to rethink it.
An alternative way of looking at it is that it's again (like in 5) bemoaning Javascript's object inequality which is prevening the memoization here.
The other 3 bullets are pretty silly "boo new things" issues that are common to migration to any new API.
by stevebmark on 4/27/20, 9:43 PM
by Saaster on 4/27/20, 3:56 PM
We have two projects, one using class components and one using hooks, and working on the class components one is unexciting, straightforward, sometimes repetitious, but never confusing. When writing hooks on the other hand it's constant gotchas; can't do that here, didn't memoize this, can't call that within this, etc. fuzzing until it works as Reacts expects. And then the bloody thing still renders on every frame. Back to the drawing board to figure out the right magic incantation. Probably memoize a few more layers, ugh.
by tenaciousDaniel on 4/27/20, 3:04 PM
- difficult to reason about except for a few simple use cases. The developer experience is nice if what you're doing is basic. But if, for example, you're aiming for 100% code coverage, unit testing hooks is an absolute nightmare.
by deckard1 on 4/27/20, 4:01 PM
Hooks reveal two major things with React:
1) React developers did not understand the component paradigm that they originally went with. If they did, then they would understand how silly it is that components cannot reuse logic. This was an entire debate many years ago. Composition vs. inheritance. You don't need to throw out classes or objects to get reuse.
2) React developers do not understand functional programming. I could write an entire essay on this. But it should suffice to say, FUNCTIONS DO NOT HAVE STATE. What React hooks introduce has more in common with dynamic scoping, of older LISPs. It fundamentally breaks lexical scoping, which is incredibly important for understanding code flow. You have to be constantly aware of when it's safe to use certain variables and the implications of using variables in certain scopes now. In 2020!! This is INSANE.
by a-priori on 4/27/20, 4:33 PM
The first point is just whining about not wanting to learn new things -- we've on-boarded many new people onto our team in this time, and hook-based code is the easiest to understand. It's the old class-based components that are hard, and the most experienced team members work there usually to rewrite them as functional components.
The second point is only sort of true. They can interact with class-based components in a parent-child relationship. That's enough to gradually migrate your application towards hooks: any time you want to make significant changes to a component, rewrite it.
The third point is not a problem in my experience. Yes, we have rewritten some of our internal libraries as a direct result of hooks being available, not because the old ones didn't work but because we now had the tools to create a _much better API_ and took advantage of it.
The fourth point makes no sense to me. If you need to use conditions like that do something different, e.g. put the different cases ("a" vs "b") in different child components and render the appropriate one. Any programming paradigm has rules around it, and this is no different.
My response to the fifth point is "don't depend on control flow". You should be robust to evaluation order so it doesn't matter the exact order that React executes your code. If you have a execution order dependency in your code it will be highly brittle.
by sibeliuss on 4/27/20, 3:40 PM
by jwr on 4/27/20, 4:28 PM
It seems to me that React Hooks, like so many things in the JavaScript world, solve a problem I do not have. To this day, despite being a heavy user of React, I don't even fully know what they do. I've read the "Motivation" section of the React Hooks Intro, and it seems that I have none of the problems they describe: I can (and do) easily add stateful logic via Rum mixins, and that logic is reusable across components. Also thanks to Rum mixins, complex logic is understandable and does not get all thrown into a single 'componentDidMount' function. As to "Classes confuse both people and machines", I find it hard to relate to this problem, because I don't really see any classes. I work with components that have a render function and mixins, and if you don't use any mixins, a component looks just like a function.
This tends to be a recurring theme: every once in a while I read an article about JavaScript and React, and I can't even relate to the problems, because they do not exist in my world. Another good example is hints on how to optimize apps to avoid excessive re-rendering, something I get for free with ClojureScript and immutable data structures (you can always quickly tell if two data structures are equal and avoid rendering).
by darepublic on 4/28/20, 3:42 AM
by twic on 4/27/20, 3:36 PM
https://crank.js.org/blog/introducing-crank
Crank itself is interesting, but what's relevant here is the broader critique of React there.
by mstudio on 4/27/20, 3:19 PM
With class components, my state/props are clearly defined within the constructor and/or PropTypes. This makes it easy to understand the overall architecture of a component. Functional components with Hooks don't have the same sort of structure and can be difficult to understand at a glance.
One of my gripes with Hooks is that listening for async state updates requires more code/complexity than w/classes. In a traditional class component, you can add a second, optional argument as a callback which is called when the state has updated:
setState({ myState: 'newValue' }, () => { this.doSomething(); });
With Hooks, that doesn't apply. The useState "set" function doesn't have a similar argument. setMyState('newState');
Instead, you need to use 'useEffect' with an optional argument: useEffect(() => { doSomething(); }, [myState]);
This leads to potentially having many "useEffects" scattered throughout the component.That said, this is just my experience with Hooks after a few months of working with them. It's entirely possible that I just haven't had enough experience with them yet.
by jasonkillian on 4/27/20, 3:16 PM
The rules do start to get really tricky though with complex use cases of `useEffect` and multiple levels of nested hooks, and implementation problems are often not easy to spot or debug.
Dan Abramov has written a lot about the philosophy of hooks[0] at his site overreacted[1], I'd love to see a 'retrospective' write-up from him or another React team member about what they think the success and failures of hooks have been so far and if there are any changes planned for the future!
[0]: https://overreacted.io/why-isnt-x-a-hook/, https://overreacted.io/algebraic-effects-for-the-rest-of-us/, https://overreacted.io/a-complete-guide-to-useeffect/
by pattrn on 4/27/20, 5:29 PM
- Hooks require substantially less boilerplate than classes.
- Rendering optimization problems with hooks tend to take more time to identify and to fix.
There are other pros/cons, but these are the ones that affect my work most frequently.
by azhu on 4/27/20, 3:14 PM
The team is pushing a functional declarative pipe method of building UI applications where things are built using a straight-line series of composed functional transformations. Personally, I think supporting this method with the hooks model of handling side effects is an improvement over everything else that exists in "roll your own" UI library land. I find these style libraries more enjoyable to build with, more expressive, and better suited to building things where you need finer grain control than template style libraries like Vue, which provide a stronger degree of predictability and ease of immediate use.
That's the thing -- it's a balance. Hooks add a nicely considered and balanced degree of order to the otherwise intentionally uncontrolled-so-you-can-do-the-controlling programming model of React. React identifies as the advanced lego set with the smaller more numerous blocks that you can build the cooler stuff with, and as such will always have a certain threshold of complexity.
by joonhocho on 4/27/20, 3:11 PM
by lpa22 on 4/27/20, 3:04 PM
by sebringj on 4/27/20, 3:20 PM
by ttty on 4/27/20, 3:11 PM
Hooks it's like learning a new language pretty much, which is only useful for react. I'm using them because of lack of better things.
by city41 on 4/27/20, 3:47 PM
by Saaster on 4/27/20, 4:13 PM
I have (a lot of) component code that will never be converted to hooks. Can I rely on you not to flake out and pull an Angular on me?
by zodiac on 4/27/20, 3:19 PM
https://overreacted.io/algebraic-effects-for-the-rest-of-us/
by mikewhy on 4/27/20, 4:34 PM
Now the two issues I have with hooks still nag me in the back of my head, but are easy to get over:
- `useCallback` still makes new function references that wouldn't happen in class-based components. as someone who starts out with `class MyComponent extends React.Purecomponent`, that bugged me.
- easily access old props after updating. I built my components with something like `useEffect`, where mounting was considered "changing props from null to _something_", and updating was like normal:
class MyComponent extends React.Component {
componentDidMount = () => {
this.handlePropsChange(null, null)
}
componentDidUpdate = (oldProps, oldState) => {
this.handlePropsChange(oldProps, oldState)
}
handlePropsChange = (oldProps, oldState) => {
if (didPropChange('userId', oldProps, this.props) {
// now we know that props.userId changed, but also have access to `oldProps.userId` in case there's any cleanup that needs to happen.
}
}
}
I know this is possible with functional components / hooks, but it was nice to get this stuff "for free".by ramoz on 4/27/20, 3:28 PM
by contigo on 4/27/20, 10:30 PM
by lpage on 4/27/20, 5:24 PM
That's true of the hooks API specifically, but not true of the underlying abstraction. Hooks are (informally) algebraic effects - one of the coolest and most general abstractions for inspecting and manipulating a program's control flow [1, 2]. Algebraic effects are still somewhat niche and most programmers haven't encountered them in name or in practice, so in that regard, hooks are actually one of the fun cases when learning a new API is mind expanding.
[1] https://github.com/ocamllabs/ocaml-effects-tutorial#1-algebr...
[2] https://overreacted.io/algebraic-effects-for-the-rest-of-us/
by psmyrdek on 4/27/20, 4:07 PM
by andrewingram on 4/27/20, 3:28 PM
One criticism of the article is that it seems to argue that you lose the ability to provide HOC (and probably render-prop) APIs if you adopt hooks in your library. But it's fairly easy to automatically turn those types of hooks into HOCs, so it actually makes sense to have the hooks API be the primary one. You can't really do it the other way around, i.e. turn a HOC into a hook.
by andrewrothman on 4/27/20, 8:58 PM
I think 1, 2 and 3 aren't really great arguments. There's always more to learn, and it seems that class components are on the way out, and are around mostly due to backwards compatibility. But it is true that a lot of legacy code uses them. I wish they'd have started with functional components, but I can't blame the team for not figuring out all of the details in advance.
I'm curious what others think. Thanks!
by ericmcer on 4/27/20, 8:57 PM
A single component wrapped by 3-4 HoC that each do trivial tasks always felt like mental strain rather than a helpful abstraction. My favorite was HoC's that added class component functionality to function components... just use a class.
by luwes on 4/27/20, 4:34 PM
https://github.com/dominictarr/observable https://github.com/adamhaile/S
Mobx and Vue use the same technique for running computeds.
As does Solid and Sinuous, etc...
by runawaybottle on 4/27/20, 3:11 PM
Don’t feel dirty for doing things simply. If your functional component has entire lookup maps for hooks, it’s probably too complicated as a standalone functional component to drop hooks in.
by rglover on 4/27/20, 3:07 PM
Hooks are like a screwdriver; great for simple stuff when you want to reduce code overhead.
Sometimes you need a power drill, though, and classes and the old-school lifecycle functions are wonderful for that.
by waddlesworth on 4/28/20, 12:55 AM
Just as one example, in a lot of posts and commentary I've seen, is that hooks are replacements for both HoCs and render props.
Admittedly, I haven't yet tried to do any actual development with hooks, but I can't even figure out how to solve the problem in the example in docs for HoCs[0].
Do you pass in a hook as a prop? That doesn't seem wise. A custom hook for each data source still has the same code duplication.
The docs talk a lot about how to build individual components using hooks, but very little about tying them together.
by chadlavi on 4/27/20, 9:42 PM
it's almost like software development is a highly skilled technical profession that takes years to master?
by bobblywobbles on 4/27/20, 4:00 PM
by Eric_WVGG on 4/27/20, 11:32 PM
don’t tell him about SwiftUI
by azirbel on 4/28/20, 3:12 AM
The main issue I have with hooks is that I can't easily trace why updates are being triggered in my app; this makes it hard to debug performance issues. For example, my app once got really slow, and the profiler told me that a root(ish)-level component was causing a lot of re-renders. Unfortunately, that component used multiple hooks, and the only way I was able to isolate the problem was by binary-searching, deleting hooks until the re-renders stopped.
Anyone have better ways of dealing with this?
by kschiffer on 4/28/20, 2:44 AM
The biggest problem I have with Hooks is readability. IMO, functional components with hooks are harder to reason about since they obfuscate logic in a weird react-specific contract. Class components have a much simpler, contract and syntax. They also felt much more natural since they picked up on familiar concepts of JavaScript, albeit with a couple of drawbacks. I get the advantages of hooks, but in a way, at least to me it seems like they, at substantial cost, solve a problem which I barely ever encountered, even after building react apps for many years.
by afranchuk on 4/28/20, 2:18 AM
by RedBeetDeadpool on 4/27/20, 5:03 PM
The other criticisms seem a little bit like someone who doesn't understand how hooks works criticizing how hooks work because he doesn't understand how hooks work. Perhaps, he doesn't understand how hooks work because he doesn't want to learn more stuff?
by abuldauskas on 4/27/20, 3:56 PM
function Component(props, { useState, useContext }) { ... }
Of course that would break backwards compatibility with the old 2nd argument being context, so I get why they did it.by luord on 4/28/20, 1:33 PM
The last two, now, do look like they can turn into serious problems (and a lot of confusing code) if one isn't careful.
Then again, I write this as someone who mostly uses, and vastly prefers, Vue so it's not like I'm an authority on react.
by mikestop on 4/29/20, 2:20 PM
by mrozbarry on 4/27/20, 8:02 PM
For instance, if I have a single app and component, and use a hook, I understand that the hook and app have some sort of implicit connection.
But what happens when I have two distinct react apps on a page - does that break the ordering that hooks require? How does a hook have any affinity to the app, or does that even matter?
I'm sure looking at the code will cause a "oh, I get it" moment, but that doesn't mean it's obvious to anyone just picking up hooks.
Honestly, I think hooks are fine, but I'd almost prefer a signature like `const MyComponent = (props, { ...hooksHere })` so there's at least a false affinity between the application and the component.
by turnipla on 4/27/20, 11:48 PM
by draw_down on 4/27/20, 3:10 PM
I guess I see a lot of this as evolutionary. It's unfortunate that there has been so much change, and the timing might not be great for some projects, but I would not prefer a world where I was still writing and using HOCs and class components.
In my day job I work on a pretty old (in React years) project, and we haven't had trouble writing new code in a functional + hooks style. Still plenty of class components abound.
by jtdev on 4/27/20, 4:36 PM