I'm going to express something a lot of people are thinking and are being far too diplomatic about.
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.
The 'magic' involved in hooks is a tradeoff; there are real benefits in the way you can consolidate logic, or mix in behaviors. Personally, I strongly prefer hooks to HOCs.
Many technologies have magical behaviors and are still very popular and useful (Rails comes to mind). I'm really liking the pros and cons being brought up in the rest of this thread.
This. So much this. You are 100% correct. Hooks are incredibly stupid. No, your component is not "functional" because you don't use the word "this". You still have a "this", it's just fucking secret now so your debugging is harder. I could go on about all the other reasons hooks are stupid, but JavaScript is largely a cargo cult and I'm a nobody so I'd just be wasting my breath.
Hooks are an elegant & clever idea but they can be difficult to use in practice. You really need to understand in detail how closures work. Manually managing your dependency graph and memoizing in all the right places is harder than the old class-based model in my experience.
I've really enjoyed working with React but it seems to me like some of the newer frameworks like Svelte have taken the best ideas from React without the baggage.
well, from my point of view i write 40% the amount of code with react hooks than i did with react classes, i probably reused about 40% more code, and can write components 50% faster than before. i also refer to React documentation about half as much as before.
not sure what's 'fucking stupid' about that.
it might be harder to grok at first - but that's the reality of tools - by nature, they get more complex but more elegant, i think it's fucking stupid to want to go back to componentDidMount()componentDidUpdate, componentWillReceiveProps, componentWillUnmount, getDerivedStateFromProps and UNSAFE_componentWillUpdate. like... really?
I never understood what was wrong with class components anyways. What did Hooks bring that couldn't be done in an easier to understand way with class components?
The React team’s claim (see my other comment) has been that by using React at all, you are already fully bought into all this magic, it’s just harder to tell.
Absolutely 100% agree; Reactive programming environments become easily unmanageable unless they are Functional Reactive Programming for the exact reason you state. Hooks are a way to manage that complexity from an imperative perspective with the illusion of a declarative veneer.
Yes. Except mobx ;)
I like to use function components together with class based observable view models. Only a single hook wires them up. Works like a charm and avoids all the IMHO confusing hook complexity.
An important point I don't see being made in the article or the comments is that hooks are meant as a more faithful (or at least less misleading) representation of what was going on under the hood in React already.
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."
It's interesting that the original appeal of React was that it was "just a view library", but now apparently it's more like a "language". It really shows the biases of the maintainers (the "just a library" thing being a philosophy I liked from vjeux, and the "language-likeness" being very obviously a heavy influence from sebmarkbage). The thing w/ "language-ness" (as opposed to "library-ness") is that additions and changes to a language tend to become more and more difficult to make over time because semantics deeply affect everything in the system, whereas well designed APIs in a library-oriented approach can be well encapsulated.
I've said for a while, for example, that throwing promises for Suspense is using up "escape hatches" in JS. The rule of hooks is another one of those. Eventually, the React team will run out of escape hatches to implement "React language" semantics around the real JS semantics, and I suspect at that point sebmarkbage will move on to create a new view framework (as has been the case w/ e.g. sebmack and babel/rome, Rich Harris and ractive/svelte, etc).
It'll be interesting to see if whoever steps up to maintain React at that point will be able to grok its internal complexity, and to see how the community reacts to a rift when their favorite view library team pushes for one vision but the moved-on "rockstar facebook engineer" pushes for a different vision.
As a user of a library, I don't really care how it works. Under the hood it can be arbitrarily complex or simple, and please feel free to change the implementation weekly for all I care. I care very deeply about my own components, when they render, what causes them to re-render, and that I can control and reason about when they re-render. Also, stability of API (in number of years) is way more important than new whiz-bang features.
> 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.
The same goes for hooks. People already understand what functions and js scope are and that leads to incorrect inferences about how hooks work.
Even more severe, newcomers who learn hooks while learning JS at the same time will get deformed perspective on how functions and scope work in JS outside of React world.
The reaction to react hooks has been (as far as I've seen) a little too positive, so I was looking forward to read a genuine critique.
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.
I initially had a few, but after making sure they weren't just learning curve exasperations in disguise, I realized they all boil down to that they should've just started out with this and never had any class components.
I often see developers mix up classes and functional components with hooks in abominable ways, and every pitfall to hooks I can find just boils down to improperly brackish OO class model polluted thinking.
It would be amazing if JS could have object equality in a performant way. I'm not sure if Python does anything interesting under-the-hood, but identical Python dicts have deep equality just fine. That would make hooks great in my opinion, where as right now they are just good. Dealing with object non-equality is like 90% of the friction I experience with hooks. Any pattern that requires me to reorganize my code (e.g. passing in individual parts of an object rather than just the whole object to the dep array) is an inelegant pattern, imo.
Hooks have unlocked so much power in React but still deserve critiquing. However I think the author only hinted at the major complaint I have about hooks, which is that it's no longer Javascript. It's not a function, it's sort of like type system magic. Hooks can't be nested, order matters, can't be conditionally called, and you have to understand trickier memoization to avoid bugs. It also isn't portable knowledge to other systems, like vanilla Javascript is. React is great because it's vanilla Javascript, expressions all the way down, and lifecycle methods, which everyone is used to. Hooks are a new non-vanilla-javascript paradigm with special and sometimes tricky rules. Other than, there's no reason to write React unless you're using hooks, and I wonder what the next major paradigm shift will be. I look back on all our HOCs and function as children and shudder compared to how easy it is with hooks.
> I look back on all our HOCs and function as children and shudder compared to how easy it is with hooks.
If by "function as children" you're referring to render props, personally I was really happy to see that short-lived fad die out. I don't think render props made things simpler.
Now if we can admit we never needed Sagas just to do some data fetching maybe we can burn that stalled-out old bandwagon, too :D
(Sagas are a powerful pattern, I'm sure someone here is about to reply about how they're making good use of them. But I'd bet 99% of people using the redux-sagas library could be doing something simpler).
I'm just speculating idly, but if you use `this`, it's clear how the function knows it's component. If you pass an explicit key, I don't think you need the order to matter. And if the order doesn't matter, well, conditional logic ought to work normally, even if it's a bad idea?
I have no idea if there's a compelling reason this wouldn't work, but if it would, it seems like it could take a lot of the magic and nonstandard behavior out of the API.
What about JSX? It’s very useful but it’s also an absolutely huge departure from vanilla JavaScript and hides a fair amount of complexity behind what your code is actually doing.
If you oppose JSX, then you should doubly oppose Angular Templating language, since that's an entire language construct that gets compiled to JS function calls. Broader point: We need some syntactic sugar on top of JavaScript for writing UIs. JSX is about as simplistic of an abstraction you can get it. It's incredibly easy to reason about its pre and post-compiled syntax. Not the case with other paradigms.
> the major complaint I have about hooks, which is that it's no longer Javascript
Are you saying that hooks are implemented in a way that needs a compiler (e.g. like CoffeeScript or TypeScript) for them to work? I've always assumed they were implemented using closures or a similar pattern.
I feel that with class components I have a really good understanding of what is rendering and most importantly, when. componentDidMount, shouldComponentUpdate, PureComponent, etc. With hooks, it's much more magic. And clarity of rendering is literally the one thing I want from React.
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.
It's a bit of a trope by now, but there is a lot of truth in the common argument that if converting your class component to hooks makes it feel more complicated, you probably had subtle bugs in your class component -- usually an edge case you hadn't bothered to handle. The main quirk of hooks is that it makes problems in your components a lot more visible. I don't view this as a bad thing, but I totally get that it's frustrating.
This is how I feel too, and I'm a confused how the reaction to hooks is so overwhelmingly positive. I find it quite strange that we need to set up an eslint rule to make sure our function arguments are correct, and it will automatically fill them out if we don't. And I need to memoize so many things! I feel like I'm not even writing javascript anymore.
This is the most legitimate criticism I've seen in the discussion. Hooks give you more control. 'useEffect' will only re-run when any value in the dependency array is updated. In class syntax, you have 'componentDidUpdate', but that function gets called after any prop or state change. With hooks, there is more granularity. Personally, I've found reasoning about hooks to be a learning curve that was conquerable in about a week. But there is no arguing, it requires you to mentally reason a little bit further than the blunt 'componentDidUpdate'.
IMO, we've traded the complexity of `this` with the complexity of hooks. Maybe I'm weird, but I never really wrote JS that caused scoping issues, so I never found `this` to be a problem. At the very least it's a complexity that is internal to the language itself. Hooks just feel so weird and alien to JS. I find them very, very difficult to reason about.
- 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.
A big problem with `this` is it is mutable. Dan Abramov has a nice article[1] explaining why that is a problem and how it leads to subtle bugs that are common in React apps. Hooks eliminate this problem, and I would guess this was one reason they decided to move forward with them.
I never had scoping issues around `this` in my react code (earlier js libraries, yes), but I still love hooks. I find them more comprehensible and consistent. I love that the API is smaller. I had to fix a number of bugs caused by the life cycle methods when I did a react 15 to 16 upgrade a while back, and it seems unlikely those can crop up with hooks.
The problem with 'this' (well, one concern): sometimes you don't have a choice. Suppose you consume a library, where you call a function, passing a function as an argument. Your function's this context can be overwritten, even if you don't desire it to be. I recently ran into this issue when consuming a third party library.
> Maybe I'm weird, but I never really wrote JS that caused scoping issues, so I never found `this` to be a problem
Either you're weird, or you've been doing JS mostly in the modern era of fat arrow functions, and fat arrow functions have succeeded at reducing the confusion from nested functions each with their own `this`.
Hooks elucidate everything I've felt wrong about React, but have not been able to put my finger on it until recently.
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.
This sort of post that asserts that nobody understood or put delicate thought into something is just pompous and lacks intellectual curiosity.
At least respond to their rationale. In doing so, you’ll find that everything is just trade-offs.
btw Dan Abramov is great to follow on twitter. He often responds to criticism and clarifies React decisions and links to good blog posts. If you use twitter it’s a nice way to get polite, bite-sized wisdom about React and Javascript. At the least you’ll realize how much good thought goes into React.
> React developers do not understand functional programming
As some sibling comments note, this is not a fair conclusion to draw. And not that it disproves your statement, but Reacts original creator Jordan Walke wrote the first React prototype in SML. Not understanding functional programming is not on the list of things I would ascribe to him. He's a smart guy.
On a slightly different note, I'd recommend anyone try out Reason. It's slowly maturing and can be a real joy, at least compared to JS/TS.
RE: 1) "it is that components cannot reuse logic" - +1 to this - recently I re-watched original hooks talk by Dan Abramov and was not able to finish it with conclusion different than "you guys really fix issues you invented before". Class-based components and reusability of logic is something that existed years before React, and probably will exist years after React. Even this concept of Dependency Injection and Services that Angular is still on proves that there-are-solutions. There are solutions for reusing logic between classes. Thing that bothers me the most is not that there's something wrong with fixing your own issues, but the fact that developers outside React Core Team start to think that "well, you cannot reuse logic between components".
Who says functions don't have state? Referential transparency requires no such constraint; it only requires that state not leak into or out of a pure function save through its arguments (inward) and return value (outward). Beyond that, what they do within the space of their own lexical scope and the lifetime of their call stack frame is entirely their own business.
I'm familiar with dynamic scoping via Emacs Lisp. I have yet to encounter anything like it in React, and it'd be surprising in any case to encounter dynamic scope in Javascript, a language which does not even support it. The closest I can come to understanding what you must mean here is to think you're very confused about how React props work, but that doesn't seem likely either - I can hardly imagine someone having such an evidently strongly held opinion about something, and having that opinion turn out to be based on a fundamental misunderstanding of the subject.
Would you mind explaining in some more detail the issues you see with React functional components? You mention having an essay's worth of material, and while that's probably more than we need to support a discussion, perhaps you'd be so good as to boil it down to a few points with a little more substance to them than "React developers don't know what they're doing" and "this is insane".
The "idea" that react hooks try to implement is very common in FP languages though. It has a lot of parallels with extensible effects; which is a pure, functional embedding of the idea of hooks
> how silly it is that components cannot reuse logic
It may be that your component is too complicated. Components should only have UI code. First move business logic out of the component, into your model layer, and make it reusable there. This step will eliminate most of the need to reuse logic in components. If you still have logic inside your component that you want to reuse consider restructuring your component into multiple simpler components.
When saying that "functions do not have state" without mentioning monads and encoding effects in type systems, it doesn't sound like you have enough experience with functional programming to assert your second claim.
Huh? You can treat state as arguments to your function. For a given function evaluation, the state is stable. The fact that state can be changed during event handling is 100% irrelevant to the evaluation. There is no dynamic scoping.
I've been using hooks full-time for the last year or so in a _very large_ React code base (one of the largest in the world). None of these so-called problems are real in my experience.
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.
In my experience people abuse react components by making them too big with too much functionality. If you have a bunch of hooks tied together in a brittle way, thats not the hooks's fault. It's a good sign that you need to refactor your component into smaller sub components or move functionality out of components all together into redux or some other non UI related code. A big component with lots of logic will always be a liability whether it's hooks or classes because it will mix presentation with business logic and will rarely be well tested.
It's unreal that in React I have to deal with occasional infinite loops now because of hooks. Sure, React catches the loop cycle so things don't totally freeze but I don't recall ever having to deal with this before them. Weird, unexpected reference issues, missing dependency accidents requiring linters to prevent, strange programming patterns, a team member having to write a terrifying novel like https://overreacted.io/a-complete-guide-to-useeffect/ for something that was never really a problem before. The list goes on and on.
>It's unreal that in React I have to deal with occasional infinite loops now because of hooks. Sure, React catches the loop cycle so things don't totally freeze but I don't recall ever having to deal with this before them ... missing dependency accidents requiring linters to prevent
Infinite loops and missing dependencies are/were issues with `componentDidUpdate`/`componentWillUpdate` and `componentDidMount` as well, though. On the plus side, you now have a linter which can both point out these errors and automatically fix them for you. I agree that the whole thing is a bit leaky and dumb though, but there's no way to fix that without introducing some sort of compilation/optimization step and afaik the React guys aren't really considering that at the moment.
>Weird, unexpected reference issues
Not sure I've run into this before. Do you have any examples?
>strange programming patterns, a team member having to write a terrifying novel
The first bit seems like personal preference or something, not sure what you're referring to as strange. The `useEffect` novel exists because a ton of people had built up their (incorrect) mental model of how React works with lifecycle methods and were making mistakes or running into unexpected behaviour because they assumed `useEffect` was the exact same thing as `componentDidMount`.
The problem before was that your class component was not updating correctly and rendering stale & out of sync data. If it were updating correctly, it would have had the same infinite loop problems.
ClojureScript user here, with a big SaaS app using React, developed over the last 4 years or so, using the excellent Rum library, https://github.com/tonsky/rum.
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).
People talk about the 'good old days' of jquery. I do think it was easier to be a web developer in those days because there weren't as many levels of abstraction. It was just your simple text editor, html and js script, that's it. And you were directly changing the DOM. But writing jQuery for complicated apps can get out of control very fast. I do not miss querying classes on an element to figure out what to do next. Nowadays I meet 'React Developers' who didn't know that you can do document.querySelector(...). They tell me without batting an eye that React makes websites faster.. and that it is faster than plain javascript. And before anyone tells me that it can make things faster through DOM diffing or what have you -- you are wrong. Situationally React could be faster than poorly written js, but in most cases it won't be and that is not its point. It doesn't magically imbue your web application with hyper speed. Quite the opposite! Its like all these layers of node, npm, React, Hooks crud built up and there are actually people junior enough that their only exposure to webdev is through this morass -- and that is sad. Not because these tools/frameworks are bad, but because web dev can be so simple and easy and they are robbed of having that in their back pocket, as a proper foundation.
This is really compelling - thanks for linking it. Is there a downside here? Has React responded at all? I don’t hate hooks, but using async + generators like this looks so obviously better and more intuitive here; like such a clearly great, simple idea that I’m embarrassed I didn’t ever think of it myself.
I have some similar gripes. I find Hooks to save a bit of coding overall. I've found my functional components to be about 10-20% smaller than my class components. I'm not 100% convinced it's really worth it, though.
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:
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.
then a week later, when you add some different code calling setState({ myState: 'newValue' }) somewhere else without remembering to add the callback, your callback won't run! Callbacks kind of break the declarative/reactive model.
Components with lots of hooks in them remind me of spreadsheets. I find myself tracing from one hook dependency array to the next, trying to follow the logic.
Hooks are great for simple use cases like `useState`, `useContext`, some uses of `useRef`, etc. and can make the code easier to read and reason about (while conveniently working very well with TypeScript).
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!
It's a unfortunate that he doesn't include the equivalent class-based implementations of his logging quiz. Event lifecycles notoriously obscure order of execution, so I'm not sure the alternative is any clearer -- especially not with contrived examples. In my experience with both hooks and classes:
- 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.
All of this reads akin to someone criticizing an apple for not being an orange. Every point is an intentional design decision. Learning new things is necessary, leaving class syntax behind was a choice, and imposing limits on (controlling) application design is the point of libraries.
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.
You didn’t actually counter any of the authors’ points.
This wonderful functional declarative pipe method of building UI applications where things are built using a straight-line series of composed functional transformations can really suck in real world applications as he tries to demonstrate. Anyone building with hooks now can relate to hooks bringing disorder to the codebase.
Has your experience been different? How did you avoid the pitfalls mentioned?
> imposing limits on (controlling) application design is the point of libraries
That's the point of _frameworks_. It's very ironic to see this being said in defense of React, given that its original appeal was precisely the opposite stance (i.e. React was "only the v in mvc", in response to the notion that frameworks of the time were imposing).
I was skeptical towards hooks when it was first introduced. I was hesitant to use it. Then, I used it for a few components in my projects. I realized how much simpler my code looked, and migrated completely to hooks. No regrets.
I am disappointed with the React team’s decision to push functional components and hooks as the standard way of working with React. Not sure if the reason is to make React more approachable to newcomers or not, but in my experience leveraging the power of the component lifecycle through class components and decorators is the most fool-proof way to build and maintain large applications. Particularly leveraging shouldCompomentUpdate for performance and componentDidMount/componentsWillUnmount for registering and disposing of component dependencies is very easy to reason about and scale.
The reason they introduced hooks was exactly that component lifecycle and decorators/higher order components were found not to scale well in larger codebases (as experienced by the people using React at Facebook).
The useEffect pretty much provides a direct replacement for componentDidMount/componentWillUnmount.
I'm still on the fence, but so far it seems to me that using hooks makes my intent clearer than using the various lifecycle methods.
My take away from hooks is that it is pushing toward making your components simpler. One of the gotchas of hooks is that it kind of "lies" in the way it looks. Take useRef or useState for example. These things are only defined one time even though the are declared in such a way to look like they are defined over and over again each render. They are actually key lookups under the hood. This was a main point of confusion for me initially and I'm sure I'll find out more that I assumed incorrectly as I go. Auto-magic sometimes is confusing to me.
They behave like class property & method declarations. But scattered about in a function and looked up by order rather than name. This is exciting and not considered redundant and obviously a bad idea, for some reason.
Hooks are magic with new rules that are different from regular JavaScript. They don't follow the regular flow you'd expect it would. Requires devs to think a lot about hooks to make sure something is messing them up. Also needing eslint to make sure your code is ok, is a boy flakey.
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.
Once you understand that function components aren't simple, contained functions but rather components that exist in a parent scope (React) and that React actively manages them, it's not magic at all. Also, you don't need ESLint; the rules are pretty simple.
Overall I think hooks are a fine addition to the React toolbox. But I think they are very easy to overuse and the complexity of hooks seems to increase exponentially. I've been involved in two code bases now where hooks are just everywhere and they were both an absolute nightmare. But I've also been involved in code bases where hooks are used more sparingly, about on par with when HoCs were used, and it's rather pleasant. In general, the more "dumb" components your app has, the more manageable it seems to be overall.
> My biggest worry with React is that it has restless developers with idle hands.
That’s exactly what I take hooks as a sign of. I read the papers and the code when they came out. I still don’t get why they exist except to provide churn to work on. A half-reimplementation of objects with a super weird syntax in a language that already has objects seems like misguided make-work on a project that’s already basically “done” except for the boring, non-flashy work of maintenance and subtler improvements.
I personally don't use hooks (or functional components) at all, but recently read this post from Dan Abramov about algebraic effects which makes a point (among others) the hook mechanism is a pretty simple way to implement state/effects/context in a language with algebraic effects.
Ah, I used to be on this side of the fence. Now I've learned to love hooks, and aced the test (except for the last, extra BS, question).
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".
After using `ember` and the wonderful `ember_data` at $PREVIOUS_FIRM, I wholeheartedly agree. React is good for what it's good for, but the community sadly did not stop there.
IMAO hooks are just a dirty hack, but sold very well. Internally in React the state of a hook is being kept and updated when you call the set function, kinda similar to vtables and context in OOP. There is no other way to do this AFAIK. It only mimics functional programming, and that's why you see the restrictions about hooks, you cannot use them outside React, cannot nest, etc..
> The problem with learning about hooks is that they're not generally applicable knowledge about computing or programming.
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.
There's yet another valuable hooks critique that I recommend you to read - https://typeofweb.com/wady-react-hooks/ (Use Google Translate to convert Polish to English)
I mean, I got full marks on the quiz in the article. I had to think about the code, but no more than if the same had been implemented as classes. I have been using React for a very long time though, but the areas where execution order can be confusing aren't a problem new to hooks.
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.
Good critique. I agree about control flow and memoization. I tend to run into issues every now and then with memoization and hook "dependencies", but I'm getting better at it.
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.
Feels like the author is someone who really sunk into the composed higher order component style of writing React. As someone who has coworkers who loved spreading logic into 'composable HoC' that only end up being reused 1-2x, I welcome hooks.
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.
Hooks are much more a reimplementation of the component lifecycle; receiving a change stream is only one of the use cases and has very little resemblance to observables, except that they trigger a re-render “automatically”.
A lot of times I just use a simple React class. The author’s lookup map to return a lookup of other hooks, yikes. A class component would probably solve that in a more predictable way.
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.
From what I've learned using them, hooks are a tool like anything else—pushing one method as "the" way means that you make a lot of poor engineering decisions.
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.
I've read about Hooks for awhile, but they still confuse me. Maybe it's largely because I haven't experienced any of the pain points that are described as the motivation for their development, but I've used a number of state-management libraries that handle state.
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.
- Giving function components the ability to have internal state and trigger side effects, giving them the same capabilities as class components have had
- Reusing logic across components
I talked about the progress from mixins to HOCs to render props to hooks in a talk at ReactBoston last year [0], which had an example of tracking mouse coordinates using all four approaches. In that sense, yes, they do replace the other techniques as a way to reuse logic.
You call them inside of your function components, like this:
function MyComponent() {
const [counter, setCounter] = useState(0);
const {x, y} = useMousePosition();
// rest of the rendering logic here
}
> The problem with learning about hooks is that they're not generally applicable knowledge about computing or programming. They're a React-specific thing. In 5-10 years when you're programming something completely different your knowledge of hooks will be completely obsolete.
I really like hooks. I previously spent a lot of time in HOCs, and I find hooks much simpler. But I also have problems with #5 (control flow):
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.
Not sure if this helps or is relevant, but I like to think of a component as a physics function.
For example, at t = 0, the output is one thing. When t = 1, the output is another. The same way of thinking can be applied to hooks. Some hooks only execute at t = 0, and at that time, variables x, y, and z also have specific values.
Hopefully you can think this way and your values won't intertwine so much that it becomes hard to trace.
Interesting to finally see some criticism about hooks.
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.
I've been out of the React game for a while, and this is the first I've read about Hooks (or at least the first time I read enough to look into them). If I understand things correctly, they are automatic dependency tracking functions that will rerun as needed? Kinda like S.js [1]? Though that's different in that it's built around only that functionality, not integrated into a larger system.
Is `more stuff to learn` seriously a critique? I mean if you want to stop learning stuff then maybe, go live in your own little bubble.
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?
My only issue with Hooks has been that they are not inputs into the component. It's a step in the right direction of making React more functional I would just preferred less magic, personally.
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.
That's not the only problem with your approach. It is extremely common to use the output of one hook in the input of another, and that's only possible if the hooks exist in the function body.
I've found hooks incredibly easy to reason about, and I'll happy take the bit of magic that goes along with it. (Anyway, I don't see any of you criticizing JSX for being magic.) On the other hand, I really like functional programming, so I've seen the entire development path of react as positive.
One thing that baffles me is that no one has brought up that hooks don't have an obvious context.
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.
The ordering question is literally just about how you call them within a single specific component. That component can then be used as many times as you want, in as many apps as you want.
Call as many hooks as you want, in whatever sequence you want. Just make sure all the calls are at the top level of that function component, and that you don't somehow change the sequence of those from one render pass to the next.
As for how they work, React already has metadata that describes each component in the tree. There's a specific field in those metadata objects that gets used for tracking internal component state, and for function components, that field stores an array / linked list of the hook calls you've made and their last saved results.
See these resources for more explanations on how hooks are implemented:
I mean, that's the whole point of hooks... they get the context of whatever host function scope they are in. That's why the 'reusable logic' spiel. So if you create a useLocalStorage hook, for example, you can then plug it into any function component and it will use. It's as if each function was an invisible class, with an invisible this.state
I don't think this is a great critique, simply because the pain I've encountered using them isn't mentioned here. The incompatibility with class components is what it is, they're different programming paradigms that you have to choose between. If your library leaned heavily into HOCs, that was an unfortunate choice and I'd recommend making a new library because HOCs were always unwieldy and had problems with composition. Nothing to do with functional components or hooks really, just a very heavy pattern that can typically be done better with another approach like render props.
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.
First of all, there is stateful code and then there is effectful code. It sounds like you are talking about the second, not the first. You can have tons of state and remain mathematically pure.
Where people get hung up is effectful code, or mutable state. Even one of the more hardcore FP languages, Haskell, does not try to abstract that out. Instead it embraces it fully by giving constructs in the language to describe and control effects! This is far more powerful than straight up imperative languages. If anything, writing mutable, effectful code is more powerful in Haskell than in C/C++.
Where Haskell gets difficult is when writing effectful code that interacts with external C libraries and the OS. But this has nothing to do with purity, state, or effects. It really only has to do with the fact that it is designed to have lazy evaluation by default. Which itself has a lot of advantages, but it makes this interaction more difficult as code does not execute in the same order as you write it.
You may find that languages such as OCaml, which are fully functional and have strict evaluation are a joy to work with.
desc|5 years ago
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.
azirbel|5 years ago
The 'magic' involved in hooks is a tradeoff; there are real benefits in the way you can consolidate logic, or mix in behaviors. Personally, I strongly prefer hooks to HOCs.
Many technologies have magical behaviors and are still very popular and useful (Rails comes to mind). I'm really liking the pros and cons being brought up in the rest of this thread.
Sophistifunk|5 years ago
cageface|5 years ago
I've really enjoyed working with React but it seems to me like some of the newer frameworks like Svelte have taken the best ideas from React without the baggage.
mx2323|5 years ago
not sure what's 'fucking stupid' about that.
it might be harder to grok at first - but that's the reality of tools - by nature, they get more complex but more elegant, i think it's fucking stupid to want to go back to componentDidMount()componentDidUpdate, componentWillReceiveProps, componentWillUnmount, getDerivedStateFromProps and UNSAFE_componentWillUpdate. like... really?
jonny_eh|5 years ago
dcre|5 years ago
nickbauman|5 years ago
ng12|5 years ago
Gehinnn|5 years ago
dcre|5 years ago
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
https://twitter.com/dan_abramov/status/1093697963350810624
https://twitter.com/dan_abramov/status/1093698629708251136
lhorie|5 years ago
I've said for a while, for example, that throwing promises for Suspense is using up "escape hatches" in JS. The rule of hooks is another one of those. Eventually, the React team will run out of escape hatches to implement "React language" semantics around the real JS semantics, and I suspect at that point sebmarkbage will move on to create a new view framework (as has been the case w/ e.g. sebmack and babel/rome, Rich Harris and ractive/svelte, etc).
It'll be interesting to see if whoever steps up to maintain React at that point will be able to grok its internal complexity, and to see how the community reacts to a rift when their favorite view library team pushes for one vision but the moved-on "rockstar facebook engineer" pushes for a different vision.
EDIT: fixed name confusion (thanks, swyx!)
Saaster|5 years ago
valand|5 years ago
The same goes for hooks. People already understand what functions and js scope are and that leads to incorrect inferences about how hooks work.
Even more severe, newcomers who learn hooks while learning JS at the same time will get deformed perspective on how functions and scope work in JS outside of React world.
traderjane|5 years ago
I wonder what about generator functions?
lucideer|5 years ago
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.
azhu|5 years ago
I often see developers mix up classes and functional components with hooks in abominable ways, and every pitfall to hooks I can find just boils down to improperly brackish OO class model polluted thinking.
dgritsko|5 years ago
fastball|5 years ago
stevebmark|5 years ago
vosper|5 years ago
If by "function as children" you're referring to render props, personally I was really happy to see that short-lived fad die out. I don't think render props made things simpler.
Now if we can admit we never needed Sagas just to do some data fetching maybe we can burn that stalled-out old bandwagon, too :D
(Sagas are a powerful pattern, I'm sure someone here is about to reply about how they're making good use of them. But I'd bet 99% of people using the redux-sagas library could be doing something simpler).
coffeefirst|5 years ago
Suppose you replace:
with I'm just speculating idly, but if you use `this`, it's clear how the function knows it's component. If you pass an explicit key, I don't think you need the order to matter. And if the order doesn't matter, well, conditional logic ought to work normally, even if it's a bad idea?I have no idea if there's a compelling reason this wouldn't work, but if it would, it seems like it could take a lot of the magic and nonstandard behavior out of the API.
untog|5 years ago
What about JSX? It’s very useful but it’s also an absolutely huge departure from vanilla JavaScript and hides a fair amount of complexity behind what your code is actually doing.
JMTQp8lwXL|5 years ago
unknown|5 years ago
[deleted]
orange8|5 years ago
Are you saying that hooks are implemented in a way that needs a compiler (e.g. like CoffeeScript or TypeScript) for them to work? I've always assumed they were implemented using closures or a similar pattern.
Saaster|5 years ago
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.
andrewingram|5 years ago
throwaway286|5 years ago
JMTQp8lwXL|5 years ago
pacala|5 years ago
Toine|5 years ago
tenaciousDaniel|5 years ago
- 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.
efdee|5 years ago
city41|5 years ago
[1] https://overreacted.io/how-are-function-components-different...
kevinmchugh|5 years ago
JMTQp8lwXL|5 years ago
brlewis|5 years ago
Either you're weird, or you've been doing JS mostly in the modern era of fat arrow functions, and fat arrow functions have succeeded at reducing the confusion from nested functions each with their own `this`.
deckard1|5 years ago
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.
hombre_fatal|5 years ago
> they do not understand
> This is insane
This sort of post that asserts that nobody understood or put delicate thought into something is just pompous and lacks intellectual curiosity.
At least respond to their rationale. In doing so, you’ll find that everything is just trade-offs.
btw Dan Abramov is great to follow on twitter. He often responds to criticism and clarifies React decisions and links to good blog posts. If you use twitter it’s a nice way to get polite, bite-sized wisdom about React and Javascript. At the least you’ll realize how much good thought goes into React.
parley|5 years ago
As some sibling comments note, this is not a fair conclusion to draw. And not that it disproves your statement, but Reacts original creator Jordan Walke wrote the first React prototype in SML. Not understanding functional programming is not on the list of things I would ascribe to him. He's a smart guy.
On a slightly different note, I'd recommend anyone try out Reason. It's slowly maturing and can be a real joy, at least compared to JS/TS.
psmyrdek|5 years ago
throwanem|5 years ago
I'm familiar with dynamic scoping via Emacs Lisp. I have yet to encounter anything like it in React, and it'd be surprising in any case to encounter dynamic scope in Javascript, a language which does not even support it. The closest I can come to understanding what you must mean here is to think you're very confused about how React props work, but that doesn't seem likely either - I can hardly imagine someone having such an evidently strongly held opinion about something, and having that opinion turn out to be based on a fundamental misunderstanding of the subject.
Would you mind explaining in some more detail the issues you see with React functional components? You mention having an essay's worth of material, and while that's probably more than we need to support a discussion, perhaps you'd be so good as to boil it down to a few points with a little more substance to them than "React developers don't know what they're doing" and "this is insane".
arianvanp|5 years ago
ng12|5 years ago
It's funny you say that: useState is the same model functional languages use to handle mutability.
https://docs.racket-lang.org/reference/boxes.html
petilon|5 years ago
It may be that your component is too complicated. Components should only have UI code. First move business logic out of the component, into your model layer, and make it reusable there. This step will eliminate most of the need to reuse logic in components. If you still have logic inside your component that you want to reuse consider restructuring your component into multiple simpler components.
stevebmark|5 years ago
pacala|5 years ago
a-priori|5 years ago
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.
gregkerzhner|5 years ago
sibeliuss|5 years ago
s-km|5 years ago
Infinite loops and missing dependencies are/were issues with `componentDidUpdate`/`componentWillUpdate` and `componentDidMount` as well, though. On the plus side, you now have a linter which can both point out these errors and automatically fix them for you. I agree that the whole thing is a bit leaky and dumb though, but there's no way to fix that without introducing some sort of compilation/optimization step and afaik the React guys aren't really considering that at the moment.
>Weird, unexpected reference issues
Not sure I've run into this before. Do you have any examples?
>strange programming patterns, a team member having to write a terrifying novel
The first bit seems like personal preference or something, not sure what you're referring to as strange. The `useEffect` novel exists because a ton of people had built up their (incorrect) mental model of how React works with lifecycle methods and were making mistakes or running into unexpected behaviour because they assumed `useEffect` was the exact same thing as `componentDidMount`.
exogen|5 years ago
dgritsko|5 years ago
jwr|5 years ago
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).
onion2k|5 years ago
Scarbutt|5 years ago
darepublic|5 years ago
twic|5 years ago
https://crank.js.org/blog/introducing-crank
Crank itself is interesting, but what's relevant here is the broader critique of React there.
wk_end|5 years ago
mstudio|5 years ago
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:
With Hooks, that doesn't apply. The useState "set" function doesn't have a similar argument. Instead, you need to use 'useEffect' with an optional argument: 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.
eat_veggies|5 years ago
If you use a callback on setState in order to listen for async state updates like
then a week later, when you add some different code calling setState({ myState: 'newValue' }) somewhere else without remembering to add the callback, your callback won't run! Callbacks kind of break the declarative/reactive model.bryik|5 years ago
What do you mean? PropTypes work just as well with functional components as they do with class components.
schwartzworld|5 years ago
it's much simpler to wrap the set function and just call your other function afterward like this:
``` const handleChange = (value) => { setMyState(value); doSomething(); } ```
city41|5 years ago
jasonkillian|5 years ago
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/
[1]: https://overreacted.io/
pattrn|5 years ago
- 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.
azhu|5 years ago
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.
jiofih|5 years ago
This wonderful functional declarative pipe method of building UI applications where things are built using a straight-line series of composed functional transformations can really suck in real world applications as he tries to demonstrate. Anyone building with hooks now can relate to hooks bringing disorder to the codebase.
Has your experience been different? How did you avoid the pitfalls mentioned?
lhorie|5 years ago
That's the point of _frameworks_. It's very ironic to see this being said in defense of React, given that its original appeal was precisely the opposite stance (i.e. React was "only the v in mvc", in response to the notion that frameworks of the time were imposing).
joonhocho|5 years ago
lpa22|5 years ago
efdee|5 years ago
The useEffect pretty much provides a direct replacement for componentDidMount/componentWillUnmount.
I'm still on the fence, but so far it seems to me that using hooks makes my intent clearer than using the various lifecycle methods.
jimbob123|5 years ago
sebringj|5 years ago
karatestomp|5 years ago
ttty|5 years ago
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.
frosted-flakes|5 years ago
city41|5 years ago
Saaster|5 years ago
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?
karatestomp|5 years ago
That’s exactly what I take hooks as a sign of. I read the papers and the code when they came out. I still don’t get why they exist except to provide churn to work on. A half-reimplementation of objects with a super weird syntax in a language that already has objects seems like misguided make-work on a project that’s already basically “done” except for the boring, non-flashy work of maintenance and subtler improvements.
zodiac|5 years ago
https://overreacted.io/algebraic-effects-for-the-rest-of-us/
jtdev|5 years ago
mikewhy|5 years ago
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:
I know this is possible with functional components / hooks, but it was nice to get this stuff "for free".ramoz|5 years ago
roosterdawn|5 years ago
tannhaeuser|5 years ago
contigo|5 years ago
lpage|5 years ago
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/
psmyrdek|5 years ago
lioeters|5 years ago
> React, like socialism, perfectly solves the problems that it created.
andrewingram|5 years ago
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.
andrewrothman|5 years ago
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!
ericmcer|5 years ago
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.
luwes|5 years ago
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...
jiofih|5 years ago
runawaybottle|5 years ago
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.
efdee|5 years ago
rglover|5 years ago
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.
waddlesworth|5 years ago
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.
[0]: https://reactjs.org/docs/higher-order-components.html
acemarke|5 years ago
- Giving function components the ability to have internal state and trigger side effects, giving them the same capabilities as class components have had
- Reusing logic across components
I talked about the progress from mixins to HOCs to render props to hooks in a talk at ReactBoston last year [0], which had an example of tracking mouse coordinates using all four approaches. In that sense, yes, they do replace the other techniques as a way to reuse logic.
You call them inside of your function components, like this:
[0] https://blog.isquaredsoftware.com/2019/09/presentation-hooks...chadlavi|5 years ago
it's almost like software development is a highly skilled technical profession that takes years to master?
bobblywobbles|5 years ago
Eric_WVGG|5 years ago
don’t tell him about SwiftUI
unknown|5 years ago
[deleted]
azirbel|5 years ago
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?
kin|5 years ago
For example, at t = 0, the output is one thing. When t = 1, the output is another. The same way of thinking can be applied to hooks. Some hooks only execute at t = 0, and at that time, variables x, y, and z also have specific values.
Hopefully you can think this way and your values won't intertwine so much that it becomes hard to trace.
kschiffer|5 years ago
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.
afranchuk|5 years ago
[1]: https://github.com/adamhaile/S
acemarke|5 years ago
I'd strongly recommend reading through the React hooks docs, as well as the other hooks resources I have linked here:
https://github.com/markerikson/react-redux-links/blob/master...
RedBeetDeadpool|5 years ago
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?
abuldauskas|5 years ago
frosted-flakes|5 years ago
luord|5 years ago
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.
mikestop|5 years ago
mrozbarry|5 years ago
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.
acemarke|5 years ago
In other words, this is not legal:
Call as many hooks as you want, in whatever sequence you want. Just make sure all the calls are at the top level of that function component, and that you don't somehow change the sequence of those from one render pass to the next.As for how they work, React already has metadata that describes each component in the tree. There's a specific field in those metadata objects that gets used for tracking internal component state, and for function components, that field stores an array / linked list of the hook calls you've made and their last saved results.
See these resources for more explanations on how hooks are implemented:
https://github.com/markerikson/react-redux-links/blob/master...
gotofritz|5 years ago
I mean, that's the whole point of hooks... they get the context of whatever host function scope they are in. That's why the 'reusable logic' spiel. So if you create a useLocalStorage hook, for example, you can then plug it into any function component and it will use. It's as if each function was an invisible class, with an invisible this.state
turnipla|5 years ago
unknown|5 years ago
[deleted]
draw_down|5 years ago
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.
jtdev|5 years ago
nightski|5 years ago
First of all, there is stateful code and then there is effectful code. It sounds like you are talking about the second, not the first. You can have tons of state and remain mathematically pure.
Where people get hung up is effectful code, or mutable state. Even one of the more hardcore FP languages, Haskell, does not try to abstract that out. Instead it embraces it fully by giving constructs in the language to describe and control effects! This is far more powerful than straight up imperative languages. If anything, writing mutable, effectful code is more powerful in Haskell than in C/C++.
Where Haskell gets difficult is when writing effectful code that interacts with external C libraries and the OS. But this has nothing to do with purity, state, or effects. It really only has to do with the fact that it is designed to have lazy evaluation by default. Which itself has a lot of advantages, but it makes this interaction more difficult as code does not execute in the same order as you write it.
You may find that languages such as OCaml, which are fully functional and have strict evaluation are a joy to work with.
TheCoelacanth|5 years ago
You could model the universe just as accurately as a stateless function of time as you could as a stateful entity that moves through time.
tome|5 years ago