
A Critique of React Hooks - vicarrion
https://dillonshook.com/a-critique-of-react-hooks/
======
desc
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.

~~~
jonny_eh
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?

~~~
kin
The problem wasn't the fact that components were classes. The problems were
the React lifecycle methods. People did some crazy things with instance
variables, shouldComponentUpdate, and componentDidUpdate, and especially the
deprecated componentWillReceiveProps.

~~~
Normal_gaussian
very much this. I had to refuse a lot of code because developers were using
these in inconsistent, confusing, and actually incorrect (read buggy) ways.

------
dcre
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."

[https://twitter.com/dan_abramov/status/1093696560280596491](https://twitter.com/dan_abramov/status/1093696560280596491)

[https://twitter.com/dan_abramov/status/1093697963350810624](https://twitter.com/dan_abramov/status/1093697963350810624)

[https://twitter.com/dan_abramov/status/1093698629708251136](https://twitter.com/dan_abramov/status/1093698629708251136)

~~~
lhorie
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.

EDIT: fixed name confusion (thanks, swyx!)

~~~
swyx
> and the "language-likeness" being very obviously a heavy influence from
> sebmack

just a minor correction, you probably mean seb markbage, who works on React,
not seb mackenzie, who made Babel and now Rome and i dont think was ever on
the React team.

i agree that when seb markbage leaves, it will be a big test of React's
legacy. I've called it the "Ship of Theseus" moment for React.

~~~
desert_boi
Got a link to Rome's docs? Babel is a little easier to google for.

~~~
pests
[https://github.com/facebookexperimental/rome](https://github.com/facebookexperimental/rome)

It's interesting. Everything is from scratch. No depa what so ever.

------
lucideer
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.

~~~
dgritsko
Agree, "More Stuff to Learn" isn't really a critique of hooks, it's a critique
of _learning_.

~~~
lhorie
TFA addresses this: it qualifies by saying learning is good as long as it's
useful outside of whatever narrow scope they appear. The real criticism in
that section is that hooks (and e.g. gotchas related to things like useEffect,
stale closures, etc) are non-transferrable knowledge.

~~~
lucideer
As others have said, I still don't really think it's applicable because
learning hooks takes an hour or two, but to try and address that point: I
don't really think the knowledge is non-transferable at all.

There's two aspects to learning hooks:

1\. Learning the API. This is not a "conceptual" part of learning but rather
"this function name does this thing". This is the same with learning any
programming API and is almost always non-transferrable (with the minor
exception of some open standards, except that varying implementations of them
still tend to have quirks).

2\. Learning the patterns and concepts around applying that API to problems.
As far as I've seen just from TFA examples, they're very widely applicable.
Memoization is widespread. Functional style is widespread. The most complex
stuff handled by the quoted examples is maintaining state in nested
hashtables, which is such a widespread concept that observable/immutable
libraries like MobX et al & ImmutableJS et al have been written pretty much
focused entirely on this problem space.

------
stevebmark
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.

~~~
untog
> React is great because it's vanilla Javascript

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.

~~~
lucasmullens
> an absolutely huge departure from vanilla JavaScript

Doesn't it just convert to React.createElement? I wouldn't call it absolutely
huge.

~~~
frank_nitti
By this logic, what could ever be considered "huge" departure? Any new
language construct "converts" to something lower level unless that language is
a set of assembly instructions. I mean, Elm converts to vanilla JS but few
would say it's a small departure from it.

~~~
hajile
That's just reductio ad absurdum claim.

JSX is one simple conversion while coffeescript or elm are entire turing-
complete languages. It's like claiming a smiley face on the back of a receipt
is no different from the Mona Lisa.

~~~
hombre_fatal
Yeah, all of the dynamic parts of JSX are Just Javascript.

You can also look at JSX and know exactly how it will compile to Javascript
because it's so simple. (foo && <Component {...props} />) -> (foo &&
React.createElement(Component, props)).

It really is just React.createElement calls. It just isn't a huge departure.

Meanwhile, what Elm compiles to is very nontrivial. The comparison only makes
sense if you never actually looked at what JSX does thus mistake it for
something much more complex.

Try it out yourself: [https://babeljs.io/repl](https://babeljs.io/repl)

------
Saaster
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.

~~~
pacala
Memoization is getting me too. I end up memoizing everything, which is more
verbose than I'd like. Perhaps we're missing some obvious pattern?

~~~
hombre_fatal
Though note that React.memo(Component) gives you the same behavior as
React.PureComponent which is what people were tending to default to anyways.

------
tenaciousDaniel
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.

~~~
efdee
Hooks weren't invented because of 'this' complexity though, but rather as a
better way to handle reuseable code (somewhat) akin to mixins.

~~~
tenaciousDaniel
I'll see if I can find the link, but I recall seeing some references in the
official docs, naming `this` complexity as a key inspiration for looking to
hooks as an alternative to stateful classes.

~~~
genezeta
It's right there in their intro to hooks in the section "Classes confuse both
people and machines" [0].

[0] [https://reactjs.org/docs/hooks-intro.html#classes-confuse-
bo...](https://reactjs.org/docs/hooks-intro.html#classes-confuse-both-people-
and-machines)

~~~
petilon
I am far more confused by hooks than by classes and 'this'.

~~~
pier25
Also you can't really try to protect JavaScript developers from understanding
_this_. It's a fundamental part of the language.

~~~
williamdclt
Disagree. I understand `this`, but I very, very rarely encounter situations
where it doesn't mean the same thing as Java's `this`. When you work on a
full-React codebase, it just doesn't really happen. We used to have weird
`this` behaviour on event handlers, but arrow functions and hooks fixed this.

Not saying it's good or bad, just an observation

~~~
pier25
Right, but without understanding _this_ one doesn't really understand arrow
functions either.

------
deckard1
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.

~~~
ng12
> But it should suffice to say, FUNCTIONS DO NOT HAVE STATE

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](https://docs.racket-
lang.org/reference/boxes.html)

~~~
iainmerrick
I think this is mostly a disagreement around terminology.

The GP is referring to _purely_ functional languages like Haskell, where
functions don’t have state and are referentially transparent. In Haskell,
useState would have to use a monad.

Racket (and Lisp in general) has mutable state so doesn’t guarantee
referential transparency. You can definitely write pure functions, and that’s
good style in many contexts, but it’s not required or enforced.

I personally agree with the GP, and assume “functional programming” to mean
_pure_ functional programming. It’s common to use an FP style in non-pure
languages, but I think this is FP if and only if you completely avoid state.

------
a-priori
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.

~~~
gregkerzhner
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.

------
jwr
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](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).

~~~
onion2k
You've solved the problem that hooks solve by using Rum mixins instead, and
you're confused why you don't need hooks?

~~~
iLemming
Have you actually used Clojurescript with React? Just any cljs library -
Reagent, Rum, Re-frame, Fulcro? Maybe try it, perhaps then you'd understand
why Clojure developers often get confused what problems every new hype cycle
in JS/TS world is trying to solve. Because Clojure idioms often nicely turn
them into something you don't have to worry about at all.

~~~
lilactown
I use ClojureScript professionally, and I believe Hooks are great. Reagent,
Re-frame all have tradeoffs that they've made to try and shoe-horn in a
solution that Hooks cleanly provides first-class support for.

I see a lot of other Clojure users wade into discussions like these and reveal
an unexamined view of the technology they use and the way that other
communities are trying to solve these same problems. It's really discouraging
to see people put Clojure(Script) and associated libs on a pedestal, because
it removes any nuance from the discussion and makes people think that the
Clojure community are a bunch of holier-than-thou zealots.

FWIW, I was (am still) super excited about Hooks and have posted a lot of
things critiquing ClojureScript React wrappers in the past, but I recognize
now that they are making tradeoffs that ultimately are what the authors think
are in the best interest of their user base. It would really be great if you
(and everyone else) would enter these discussions with the same assumption.

~~~
jwr
Ok, so, what problem do Hooks solve for me, as a ClojureScript/Rum user?

(apart from compatibility with other libraries that use them)

~~~
lilactown
Here's a rundown on mixins vs. hooks:
[https://reactjs.org/blog/2016/07/13/mixins-considered-
harmfu...](https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)

I haven't used Rum in anger, but I do use reagent at work, and there are
compatibility tradeoffs not just with other libs but also React itself that
storing lots of state outside of the component tree in a mutable ref causes.

There's potentially a lot of work to be done to support React concurrent mode,
which has a measurable impact to the user experience of apps that we build.
Following best practices set by the React team - including using hooks for
sharing and composing behaviors - sets us up better to take advantage of that
in the future.

Ultimately though you don't need to rewrite your Rum/reagent apps, but instead
there's an onus on the maintainers of those libs to build scaffolding around
their lib code and encourage their users to migrate in a direction that will
benefit them.

------
darepublic
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.

------
sibeliuss
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/](https://overreacted.io/a-complete-guide-to-useeffect/) for
something that was never really a problem before. The list goes on and on.

~~~
s-km
>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`.

~~~
sibeliuss
Reference issues:

1) Needing an advanced understanding of closures. Not always, but sometimes.
That "sometimes" is often unintuitive, requiring weird solutions like useRef.
Good luck beginners.

2) Things like updating reducer state by using a spread object, which creates
a new object which can then send a system haywire. Seems fine, and is mostly
fine in most cases, but hey, oftentimes not fine, and why that's so is not
always clear. So then there's memoization, and useCallback and all of these
safety checks -- each with their own dependencies array that need to be
checked. It's really too much tbh. There are lots of solutions out there that
use proxies to check this stuff; React should have baked that into the core
and completely removed that responsibility from the user unless they wanted to
opt-in micromanage performance of their code.

~~~
ng12
> Things like updating reducer state by using a spread object, which creates a
> new object which can then send a system haywire

This is what you should be doing and not doing this seems more likely to cause
problems. Do you have an example?

------
twic
I learned about Crank today:

[https://crank.js.org/blog/introducing-
crank](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
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.

~~~
aero142
There was a lot of discussion in the Crank thread. Essentially, concurrent
mode is the primary goal.

[https://news.ycombinator.com/item?id=22903967](https://news.ycombinator.com/item?id=22903967)

------
mstudio
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:

    
    
      setState({ myState: 'newValue' }, () => { this.doSomething(); });
    

With Hooks, that doesn't apply. The useState "set" function doesn't have a
similar argument.

    
    
      setMyState('newState');
    

Instead, you need to use 'useEffect' with an optional argument:

    
    
      useEffect(() => { doSomething(); }, [myState]);
    
    

This leads to potentially having many "useEffects" scattered throughout the
component.

That said, this is just my experience with Hooks after a few months of working
with them. It's entirely possible that I just haven't had enough experience
with them yet.

~~~
schwartzworld
That's a misuse of useEffect.

it's much simpler to wrap the set function and just call your other function
afterward like this:

``` const handleChange = (value) => { setMyState(value); doSomething(); } ```

~~~
mstudio
This does not work. setMyState() is asynchronous. doSomething() would run, but
not necessarily with the updated value of the state.

~~~
schwartzworld
Then make the wrapper function async and await setMyState?

------
jasonkillian
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!

[0]: [https://overreacted.io/why-isnt-x-a-hook/](https://overreacted.io/why-
isnt-x-a-hook/), [https://overreacted.io/algebraic-effects-for-the-rest-of-
us/](https://overreacted.io/algebraic-effects-for-the-rest-of-us/),
[https://overreacted.io/a-complete-guide-to-
useeffect/](https://overreacted.io/a-complete-guide-to-useeffect/)

[1]: [https://overreacted.io/](https://overreacted.io/)

------
pattrn
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.

------
azhu
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.

~~~
jiofih
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?

~~~
pacala
1\. It's not more stuff to learn, it's _different_ stuff to learn, arguably
less. Learning Components is not a prerequisite for learning Hooks. Or perhaps
I missed the memo, as I've built an app using Hooks, and still haven't need to
learn what 'componentWillMount' is supposed to do.

2\. Don't mix Components and Hooks.

3\. Agreed, change is hard. It's also the only way to avoid stagnation. In the
long term, change wins. Or else we'd be programming in JS1995.

4\. Insufficient example. What is the business case for a memoized hook
returning hooks? Perhaps there is a simpler design, can't comment.

5\. There is no global control flow. There is only per function component
control flow, which proceeds from top to bottom. Possibly preempted by a
hook/hookfn execution, if my early learning curve is to be believed. Which
shouldn't matter if one is thinking in terms of 'pure functions returning
jsx', as preempted functions do not return, thus have no observable effect.

Tip: Only change hook state from event handlers, never from render function
code.

~~~
Scarbutt
Unless you learnt react from non-official sources, you can't avoid learning
components because they teach them first.

~~~
pacala
You'd be surprised. The only deeper dive into Components is
[https://reactjs.org/docs/state-and-
lifecycle.html](https://reactjs.org/docs/state-and-lifecycle.html) of the
"Main Concepts" section, which I skipped in favor of
[https://reactjs.org/docs/hooks-state.html](https://reactjs.org/docs/hooks-
state.html) of the "Hooks" section. Haven't got to "Advanced" yet, hope to
stay clear of that.

------
joonhocho
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.

------
lpa22
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.

~~~
efdee
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.

~~~
didericis
Can you provide links to articles where react devs detail the scaling issues?

I've found HOCs easy to combine and reason about if I name them carefully, and
am still using them on personal projects. When people complain about HOCs not
scaling well, are they primarily complaining about name collisions, or
performance issues due to deeply nested components/lots of render calls?

~~~
acemarke
It's primarily about naming collisions and indirection.

I covered some of the tradeoffs in this post and talk:

[https://blog.isquaredsoftware.com/2019/07/blogged-answers-
th...](https://blog.isquaredsoftware.com/2019/07/blogged-answers-thoughts-on-
hooks/)

[https://blog.isquaredsoftware.com/2019/09/presentation-
hooks...](https://blog.isquaredsoftware.com/2019/09/presentation-hooks-hocs-
tradeoffs/)

------
sebringj
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.

~~~
karatestomp
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.

------
ttty
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.

~~~
frosted-flakes
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.

------
city41
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.

------
Saaster
My biggest worry with React is that it has restless developers with idle
hands.

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
> 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.

------
zodiac
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.

[https://overreacted.io/algebraic-effects-for-the-rest-of-
us/](https://overreacted.io/algebraic-effects-for-the-rest-of-us/)

~~~
jtdev
Yay, just what we all needed to help us write clean, maintainable code...
algebraic effects??

~~~
zodiac
Honestly? Maybe it is, maybe it isn't. Imagine replacing "algebraic effects"
in your question with "exceptions" \- theres no clear answer. What do you
think?

------
mikewhy
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".

------
contigo
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..

------
lpage
> 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.

[1] [https://github.com/ocamllabs/ocaml-effects-
tutorial#1-algebr...](https://github.com/ocamllabs/ocaml-effects-
tutorial#1-algebraic-effects-and-handlers)

[2] [https://overreacted.io/algebraic-effects-for-the-rest-of-
us/](https://overreacted.io/algebraic-effects-for-the-rest-of-us/)

------
ramoz
React is great at rendering data. It should've stopped there.

~~~
roosterdawn
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.

------
psmyrdek
There's yet another valuable hooks critique that I recommend you to read -
[https://typeofweb.com/wady-react-hooks/](https://typeofweb.com/wady-react-
hooks/) (Use Google Translate to convert Polish to English)

~~~
lioeters
Hahah, this is good.

> React, like socialism, perfectly solves the problems that it created.

------
andrewingram
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.

------
andrewrothman
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.

I'm curious what others think. Thanks!

------
ericmcer
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.

------
luwes
Hooks are nothing new, it's just repackaging of

[https://github.com/dominictarr/observable](https://github.com/dominictarr/observable)
[https://github.com/adamhaile/S](https://github.com/adamhaile/S)

Mobx and Vue use the same technique for running computeds.

As does Solid and Sinuous, etc...

~~~
jiofih
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”.

------
runawaybottle
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.

~~~
efdee
It's hard to reason about without an actual real-life use case, but the lookup
thing he's doing looks extremely convoluted to me.

~~~
WorldMaker
Right, I'd be curious to know a lot more details of the real-life use case
that inspired that. My gut feeling says that there's maybe a state machine of
some sort which could possibly be split into a sequence or maybe hierarchy of
smaller components, but it's hard to tell specifically what the alternative
might be without more details on why they thought a lookup table might be
useful.

------
waddlesworth
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.

[0]: [https://reactjs.org/docs/higher-order-
components.html](https://reactjs.org/docs/higher-order-components.html)

~~~
acemarke
Hooks solve a couple different problems:

\- 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
        }
    
    
    

[0] [https://blog.isquaredsoftware.com/2019/09/presentation-
hooks...](https://blog.isquaredsoftware.com/2019/09/presentation-hooks-hocs-
tradeoffs/)

------
rglover
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.

------
chadlavi
> so much to learn

it's almost like software development is a highly skilled technical profession
that takes years to master?

------
bobblywobbles
This is why I avoid hooks. I design my components to use state and I find
hooks to not fit well in my applications.

------
Eric_WVGG
> 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.

don’t tell him about SwiftUI

------
azirbel
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.

Anyone have better ways of dealing with this?

~~~
kin
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.

------
kschiffer
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.

------
afranchuk
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.

[1]: [https://github.com/adamhaile/S](https://github.com/adamhaile/S)

~~~
acemarke
Not exactly.

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...](https://github.com/markerikson/react-redux-
links/blob/master/react-hooks.md)

------
RedBeetDeadpool
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?

------
abuldauskas
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.

~~~
frosted-flakes
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.

~~~
abuldauskas
I don't really understand how this approach breaks what you are describing.

All I'm saying is that instead of hooks API being imported from React at
global scope it could be provided as inputs into the components directly. They
would still exist in the function body as you put it.

~~~
frosted-flakes
Oh, I thought you meant that the hooks would be _called_ there, which was one
of the many alternative proposals made after hooks were announced.

In any case, it still wouldn't work because hooks are composable. You can
create your own custom hooks outside of components which can be used as if it
was one of the primitive hooks. That's not possible if the primitive hooks
can't be accessed outside the component scope, unless they're passed in as
parameters every time the custom hook is called (and that would be a right
pain in the backside).

------
luord
The first three points are something that crops up in any moderately used
library/framework, one way or another. Seemed a little bit like nitpicking.

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
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.

------
mrozbarry
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.

~~~
acemarke
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.

In other words, this is not legal:

    
    
        const [stateA, setStateA] = useState(false);
    
        if(stateA) {
            const [stateB, setStateB] = useState(42);
        }
    

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...](https://github.com/markerikson/react-redux-
links/blob/master/react-hooks.md#understanding-hooks-concepts)

------
turnipla
I never got into React, but seeing it compared to Svelte I find it hilariously
convoluted. Hooks make it even worse.

------
jtdev
The universe is stateful; no matter how hard FP zealots try to abstract that
out of code, they will never change this fundamental fact.

~~~
TheCoelacanth
Nonsense. These are all just techniques for modeling reality, not reality
itself.

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.

~~~
jtdev
The further you get from the true representation of the thing you’re trying to
model, the more difficult reasoning about it becomes. I’m all for abstractions
that result in a net positive... I simply believe that FP is less about
improving computing and more about ego.

------
draw_down
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.

