Nice resources! I've bookmarked them for later study.
That last one I like, especially where the author recommends to "forget everything about lifecycle methods" and think of hooks in the context of synchronization. It makes sense, as a declarative way to describe state and state transitions.
The aspect that's unsettling is that they're not idempotent pure functions, but rather deeply tied in with how React works internally. They can't be used outside of React, and require the programmer to understand the magic that makes them stateful.
The common issues that beginner users of hooks encounter, like the "captured scope" of variables, or that hooks must be run in the same order every time, never conditionally - I suppose these are some reasons that make me (and other hook skeptics) react to them as "code smell". If someone had designed a library totally unrelated to React this way, I wouldn't want to use it.
Looking at ReasonML, it's quite elegant and intuitive how React Hooks fit in. In fact, the code examples look very similar to how I structure my React projects, with state and actions (instead of a reducer or Redux, they use immer for immutable state changes).
I'm staying open-minded about hooks, and I think the more I learn of its roots, the more I'll come around to accepting them as part of idiomatic React.
The most telling difference after starting to use hooks when working with both junior developers and more seasoned ones is that code that seemingly (and intuitively) behaves in one way actually does something slightly different, or worst case - something entirely different. Most often the culprit is a combination of the hooks themselves, the rules and the more general issue of the dependency array and lack of built-in immutability in the language.
This happened before hooks as well, but the class syntax and lifecycle methods felt like a thinner layer on top of JavaScript. Hooks is a much more proprietary concept that tries to solve a much wider problem - having stateful functions, except they make no sense outside the realm of react as they exist right now. Maybe some form of implementation using generators would bring it closer to the language, but that would most likely introduce it’s own set of challenges.
Don’t get me wrong - I enjoy working with hooks, but it just doesn’t feel quite right that they are so tied to some «magical» implementation that requires a large set of rules to behave as expected. It helps to look into the implementation, and especially to reimplement a simple version of react with hooks yourself - but that’s just not a realistic option for many.
Kudos to all the innovation coming from the React team and community though - I’m sure they think about this stuff all the time.
I highly recommend this talk (first half) for a great introduction of how React Hooks work internally, Ryan remakes the basics of hooks from scratch in 30 min:
I agree on 2 of the points of Jared Palmer but disagree on one:
- [agreed] The new "Concurrent Mode" will take things to a new order of complexity. I am actually very afraid of this, it seems like one of the things that might actually kill React, and I love working with React. I like it so much that I've invested into creating few React libraries to make my life easier.
- [agreed] React Hooks are difficult to get started with and to really understand them. IMHO this is mainly due to "React lifecycle", which is just a difficult concept (even with classes!). Life is not easy, and there are some times that there's complexity and you need to learn new complex concepts. No issue here IMHO.
- [disagree] React Hooks is currently magic. While it's a complex piece of software with many small details, once you understand fairly well the lifecycle (and React docs are great at this, and Dan Abramov's https://overreacted.io/ digs a lot deeper for the curious) all bugs are very clear.
I am currently mentoring someone in React who already knew HTML+CSS+JS, my recommendations are: get used to the syntax by practicing, learn the 3-4 typical libraries, and learn very well the lifecycle including how useState's setState and useEffect's dependencies work. That's most of day-to-day React work.
Thank you for the thoughtful reply. I'm a fan of your work and writings, they've been valuable for the whole JS/React ecosystem.
> you could probably implement a simple version of Hooks yourself in <200 lines
It reminds me of the Egghead video course in which you described "how to write your own Redux". That was a great explanation of the paradigm, and transformed how I think about state management in an application.
What I love about the concepts behind the paradigm, is that they can be implemented (as they are in Redux and elsewhere) as a handful of tiny functions that work with or without React, for composable and reusable state and actions.
I think my hesitation about Hooks as they're implemented, is that it seems to go against the ideal of de-coupled, pure functions. Although it does encourage that for the users of hooks, its own implementation feels coupled and impure.
For example, people are applying the Flux/Redux paradigm outside of React components and the UI/views layer, like server-side or independent features that need to manage their own states. I wish that Hooks had taken a similar approach to Redux, as a canonical approach to a generic and generally applicable paradigm, where one could "write your own Hooks".
> I strongly encourage you to play with it before forming a final opinion. Pretty much everyone who loves it now hated it at first. (I’m not an exception to that.)
Yes, I'll keep an open mind and study it more.
I read "React as a UI Runtime" when it was published, and it was insightful (will read it again to really digest it). I do love the concepts and principles on which React has been built, so I'll try to understand the reasonings behind how/why Hooks were implemented the way they are.
It is also quite useful to check https://ahooks.js.org/hooks/ directly: Observing that there are hooks for seemingly basic tasks, and reading their linked code, makes it easier to figure out more React anti-patterns, simply by seeing what the hooks author did instead.
I really wish the official documentation was better. Often the docs are self-referential, referring to how current approaches are better than thing X React did in the past, and then the trail gets lost, instead of explaining from first principles how things work and why they are done the current way.
> I think the old way is simpler in the sense of building on fewer concepts, and therefore allowing an easier learning curve
I would disagree with that premise. From my own experience and from having mentored developers on class components (so like ~7 data points), I'd say it's far easier to reason about state flow with hooks than lifecycle methods. That's an important point, because I see far more bugs with incorrect or incomplete state flow than I see with component lifecycles.
Hooks are simply easier to reason about. With hooks, you read the code top to bottom. Then, something changes either with the data or in response to user input, and then you read the code top to bottom again with that new state.
The only learning curve there is to understanding the basic premise of hooks is that "if this value is different from the last time you read the code top-to-bottom, then re-read/execute this code block"
> If using hooks allowed developers to avoid knowing the React component lifecycle, an argument could be made that they provide a simpler alternative, but I don't think anybody is going to get very far without being forced to learn about the component lifecycle. If someone were to say, "The React lifecycle is hard to understand, so use hooks instead," that would be a false promise.
That is exactly my premise, and I don't think it's a false one. Since moving to hooks, I can't remember the last time I had to care about the React lifecycle. Again, the mental model is "Read the code top to bottom; something changes; read the code from top to bottom" ad infinitum.
Yes there are situations where class components provide more intimate control (componentWillUnmount comes to mind since I'm not aware of a hook for that), but are we really optimizing the majority of our code for exceptional cases?
Another data point: I haven't seen functional components turn into a complete mess from inattentive developers in the same way I've seen class components turn into indecipherable setState tangled messes. The only time I've experienced the pressure of sifting through component lifecycles under the pressure of debugging a time-sensitive production isssue is because I was using component lifecycles in the first place. It's a moot point.
After reading numerous articles (starting with the docs), I'm still not convinced, and will not be using React Hooks any time soon. Some of the things I do not love:
- Magic, in the unfavorable sense: if hooks were implemented in an indepedent library, just hearing that they "must be called in the same order every time" and "cannot be used inside conditional statements" should make anyone wary.
- Scope creep: if anything, I wish React focused on reducing its size and API surface.
- Entanglement: related to the point above, the goals of hooks would have been better served with a separate, tiny library that doesn't need to know anything about React, and usable anywhere else, with plain functions that can be tested or reused independently.
Well, the topic deserves a deeper criticism than "it feels wrong", so I hope someone can write a worthy article called "Why I Do Not Love React Hooks", with an example of a better, alternative solution.
I hesitate to bring up a cliché, but it's starting to feel like React is the new jQuery. I'm already trying to anticipate what comes after React, in which case I want my code to depend on the smallest possible API surface area.
But then again, maybe it's my wishful thinking that we would move towards "universal components" (using dependency injection to pass in React or other view renderers like Web Components). Since much of the JS ecosystem seems to be going "all in" on React, perhaps I should surrender to its current. As long as I continue to use React, it looks like there's no escape from its hooks..
EDIT: I should add that the above is just my opinion at the moment, and I'm open to changing my mind. Who knows, I may unwillingly start using them (since everyone else seems to be), and come to love hooks after all.
> React hooks are an interesting and tricky thing to put in a box, because they’re almost monads, almost thunks, almost algebraic effects. The interplay with fibers and components makes them hard to pin down, but they are much closer to functional than any other paradigm.
They're almost classes (/objects). The implementation is what it'd look like if you wanted to make a weird half-assed OO... thing. Property and method declarations, getting attached (ultimately) to an object. But it's FIFO access/calling, rather than using named properties and methods w/ a lookup step. And the declaration syntax is bizarre and hard to read. And they're slower than regular JS OO (because they add an extra layer of JS on top of it).
Is there anyone who would argue that React Hooks are "intuitive", or at least become that way after you've used them for long enough? Genuinely asking.
I worked with React for several years and I've worked with the web for almost a decade, but when hooks came out I immediately "noped" out of there. Not that I couldn't learn them, but they just felt like a deeply contrived way of managing state changes. I'm comfortable with a functional style for lots of things, but when it comes to your actual core application state, FP has always seemed very out of place to me. It's almost antithetical: FP's whole thing is being stateless! It feels like a square-peg-in-a-round-hole situation.
By contrast, using something like MobX for reactivity combined with as-stateless-as-possible class-based React components (and some totally-stateless functional components!) was extremely easy to reason about and scaled really well for us with almost no boilerplate.
I realize that doing something like concurrent-mode requires some unique accommodations, but I just don't understand the big push for hooks. Do they only exist because of concurrent mode? And if so, was there really no other option for getting the same results?
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.
I've also had doubts about hooks since its proposal, and I agree with the points you raised.
From the Rules of Hooks section in the documentation[0].
- React relies on the order in which Hooks are called.
- Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
- Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions.
This design decision doesn't sit well with me, and I won't be using it. However, the community seems to have already welcomed it, so I'm forced to learn the intricacies to be able to understand React codebases and keep up with the latest trend..
The addition of this feature is sure to increase cognitive load and technical debt. There are already countless variations of state management libraries and patterns that encourage pure functional components and decoupled logic. Hooks seem to be yet another opinionated strategy - and not for the best, in my view.
wow thanks, chatmasta, I am really glad to hear this great feedback! I would like to put the credit towards React Hook because it really saves us to write less code and not to worry too much about the lifecycle methods.
I think most specifically the call out in the article is: the Hooks that React provides are extremely "low-level" (and intentionally so) and while you can do everything with just raw low-level hooks, consider returning to higher level hooks. The article provides some simple (Typescript type-safe examples) of higher level hooks. It also recommends that Redux/MobX/Relay/etc are still very useful higher level tools, even or especially in hook-based components in React.
You're very welcome. I do believe looking at reasonml and reasonreact is a good way to gain insight into reactjs. It's on my (so very long) to-do-list.
Ed: as an example, I found this (oldish) post on hooks in reasonreact:
Finally I came across this - I thought maybe a sibling comment mentioned it - but a quick search didn't turn up anything - but imo it's a pretty strong argument for hooks (in js react) :
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.
> weird step into "magic" for a library that has been a pretty damn thin layer on top of JS
I mean, hooks are totally this. They fundamentally break the concept of a "function" despite calling them "functional components." They use this weird notion of state (i.e. magic). The best way I can describe it is a cousin of dynamic scoping from the bad old LISP days. Back before lexical scoping was invented. So you end up with all these caveats and gotchas. Go look up useRef with useEffect and useCallback. It's all a total clusterfuck.
The lifecycle methods weren't perfect. But at least you could understand them. But I lost a lot of respect for React way back when they started fussing with the lifecycle API, a year or two before hooks. Their vDOM abstraction was starting to leak heavily and they were struggling to make sense of it.
When people say they like React, I really suspect they mean they like JSX. React, itself, is a mess. Always has been.
When Hooks are not properly understood, blog posts like this one make hooks look bad. However, that is just an error from the author, which results into unnecessary and overly complicated "solutions".
Yes, Hooks take a bit of getting used to and until you get them they will be ... weird. I did a few mistakes trying hooks until I actually "got" them (the fact that I tried to use hooks without actually understanding them certainly didn't help). But learning to do them right was certainly not harder than learning the quirks of classes.
In the beginning I thought they complicate the code unnecessarily.
Until I realized they can just be extracted into custom hooks.
The real power of hooks, besides the fact they are declarative!, is composition and by means of composition they can be abstracted away as custom hooks.
React is all about declarative UI composition but lacked the “primitive” for having stateful logic composition in an elegant and declarative way.
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.
That last one I like, especially where the author recommends to "forget everything about lifecycle methods" and think of hooks in the context of synchronization. It makes sense, as a declarative way to describe state and state transitions.
The aspect that's unsettling is that they're not idempotent pure functions, but rather deeply tied in with how React works internally. They can't be used outside of React, and require the programmer to understand the magic that makes them stateful.
The common issues that beginner users of hooks encounter, like the "captured scope" of variables, or that hooks must be run in the same order every time, never conditionally - I suppose these are some reasons that make me (and other hook skeptics) react to them as "code smell". If someone had designed a library totally unrelated to React this way, I wouldn't want to use it.
Looking at ReasonML, it's quite elegant and intuitive how React Hooks fit in. In fact, the code examples look very similar to how I structure my React projects, with state and actions (instead of a reducer or Redux, they use immer for immutable state changes).
I'm staying open-minded about hooks, and I think the more I learn of its roots, the more I'll come around to accepting them as part of idiomatic React.
reply