
Thinking in React Hooks - mariuz
https://wattenberger.com/blog/react-hooks
======
oaxacaoaxaca
I have not been a fan of hooks thus far, but these are some intriguing
examples. Especially the useMemo ones.

My complaints about hooks are that with hooks we lose useful tools in our
react toolkit: class methods, separate componentDidMount/componentDidUpdate
lifecycle events, shouldComponentUpdate, PureComponent, etc.

And my biggest complaint is that we're losing simplicity. React spread like
wildfire when it first came out because it was so beautifully simple: a class,
with something called `props`, and this.state and this.setState(). That's it.
You can build SO MUCH with just that.

To me, with hooks, React's simplicity is being replaced by cloudiness. There's
so much "magic" now. The order of hooks in the code is the most important
thing for a hook component to work right. New linter rules had to be built to
remind people where hooks need to be written.

I do appreciate the work that's been put into hooks. And like any other open
source project, I'm incredibly grateful to the authors and maintainers. I
guess I just wonder if anyone shares the same sentiments as mine, because it
seems like the only thing I read about this topic is that classes suck, class
components are full of pitfalls, etc.

~~~
lilactown
You use the word "simple," but it sounds like you mean something more like
"familiar." That's not a knock; familiarity is important when designing an API
that thousands of developers are going to use every day.

But as you can see by the blog post, there are tradeoffs between doing
something the way people are familiar with, versus a different way that's less
familiar, but has different capabilities that might fit the problems better.

Classes map to certain use cases very well; for instance, there's no Hook for
"componentDidCatch" or any other error handling.

That being said, you are not losing anything. The tools are simply different
(although the old tools are still there, too!).

As the blog post goes into, you can still implement the same _behavior_ as you
did with componentDidMount/componentDidUpdate, but with Hooks you can
formalize the different patterns you would find when using those methods into
a custom hook that you can reuse. This wasn't possible before.

shouldComponentUpdate can be easily replaced with React.memo, a higher-order
component that takes a function that compares the previous props to the new
props and returns whether they are the same. Wrapping it in React.memo without
a custom comparator is the same as PureComponent.

~~~
dkarl
I think the old way is simpler in the sense of building on fewer concepts, and
therefore allowing an easier learning curve, if you grant that understanding
the React component lifecycle is required and inevitable. Once you understand
the lifecycle, running code when certain lifecycle events happen is a very
small, concrete conceptual leap. Names like "componentDidMount" tie directly
into your fundamental understanding of React. If using them to maintain state
forces you to deepen your understanding of the component lifecycle, that
effort will pay dividends when you have to debug, optimize, understand someone
else's wacky code, etc.

Hooks, on the other hand, have to be learned as an additional and separate
concept. In fact, each hook has to be learned. You won't have a clue what
"useState" and "useEffect" do just by having a knowledge of React
fundamentals. 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. Hooks are something extra to learn, not an alternative to
understanding the lifecycle. To the extent that a programmer can avoid
understanding the lifecycle by using hooks instead, they are just putting off
that learning curve until they have to traverse it later, probably under the
pressure of debugging some nasty issue.

Hooks seem to win from another perspective, though, which is that by accepting
the cost of adding this new concept to your mental model, you can write
simpler-looking code that is easier to keep error-free. To me, that's the
trade-off. Hooks are an extra complexity to understand, and each new hook can
encapsulate arbitrary complexity and behavior that isn't obvious from reading
code that uses them, but the code that uses them can be more concise and
perhaps easier to read and keep correct.

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

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

The return value of invoking React.useEffect’s first parameter is a cleanup
function which serves this purpose.

Everyone in this thread is ragging on core concepts like useMemo or useEffect.
Once you know these core functions, you’ve learned _all_ the magic. Then you
can write spells of your own.

------
stephen
What an amazingly well-written, well-illustrated blog post.

I really like how the post showed the contrast between "imperative anytime
thing x changes remember to poke thing y" vs. "declarative thing y is calc'd
on thing x".

That said, I'm a little disappointed with hooks b/c, while React is not at all
claiming they invented this declarative approach, it seems like other "state-
of-the-art" approaches (like mobx? not picking that one as the best/only, but
it's observer-/push-based) can do the same thing without the ugly duplication
between "here's my calc on x/y/z" \+ "oh and my calc's array of dependencies
is [x,y,z] which btw also doesn't use deep equality".

Like I get that it works, and it's built into React, so it's a for-free
declarative/derived value/derived state approach vs. pulling in yet another
framework, but I'm disappointed they couldn't (yet?) solve the "you also have
to specify your dependencies/shallow equality" problem.

~~~
WorldMaker
I've worked with frameworks all over the map in terms of dependency tracking,
and right now I'd lean toward React's manual dependency tracking is probably
the better approach _as the default_. Making it manual does mean that it is
easy for silly mistakes to be made (missing a dependency or adding one too
many), but it's a learning/debugging trick and eventually starts to become
second nature. Tools can be built on top of this manual tracking to better
automate it, such as edit/compile/pack time linters ("warn: you use this
variable in this callback but don't include it as a dependency"), or
additional meta-hook libraries at runtime that do some of the automatic
dependency tracking magic (through Proxy objects or what have you).

It's harder to fix some classes of mistakes/performance issues in the more
automatic systems, and the classes of mistakes in the manual dependency
tracking world are often easier to spot ("why isn't this updating?" when
forgetting a dependency as opposed to "why is this updating too often?" with
automatic tracking).

I wouldn't be surprised if there aren't more automatic tracking tools that
show up built on top of the basic hook pattern.

My biggest complaint with the React dependency tracking is that empty
dependency array [] has different behavior from null/undefined dependency
array and so far I always have to look up which one I actually need and I just
haven't yet written enough hooks that I've internalized why which behavior is
which. It keeps the hook functions terse to write, but it's a small papercut.

~~~
ratww
> My biggest complaint with the React dependency tracking is that empty
> dependency array [] has different behavior from null/undefined dependency
> array and so far I always have to look up which one I actually need

I wish they had used a constant, like "React.everytime" instead of null. I
rarely want an useEffect to fire on every render, but wanting it to fire on
mount is very popular, to the point a lot of people have an `useMount` custom
hook...

------
throwaway286
Reasons I dislike hooks: The dependency array feels like a hack. It's way too
easy to accidentally make an infinite loop. It's no longer safe to pass around
functions; they have to be wrapped in useCallback(), which also has a
dependency array. Yes, I'm aware of the eslint rules and the autofixer. It
just feels that I'm so far away from writing javascript. Hooks are great when
you're doing a simple setState or something, but with complex logic they get
confusing quickly.

~~~
tekkk
Your comment reminds me, I never found a perfect explanation what a negation
inside the dependency array does. Does it simply negate the checked variable
or just omits it from ever triggering eg useEffect? For example:

    
    
      const useClickOutside = (ref, onClickOutside, isActive) => {
        ...
        useEffect(() => {
          if (!isListening && isActive) {
            addHandlers(handler)
            setListening(true)
          } else if (isListening && !isActive) {
            removeHandlers(handler)
            setListening(false)
          }
          return () => {
            removeHandlers(handler)
          }
        }, [ref, !onClickOutside, isActive])
      }
    

I remember adding the negation here as the useEffect would fire needlessly (or
wouldn't, can't remember), but wasn't entirely sure what it did. The whole
hook is just for checking clicks outside an element.

Anyway, I agree with you that at times they can a bit too abstract for the
fellow programmer. Without prior knowledge how hooks work it can be quite
tedious to read (without proper documentation at least)

~~~
emilecantin
In this case, onClickOutside was probably a function that you defined inline
in your parent component, which would actually be a new thing on each render.

(Something like <Blabla onClickOutsite={() => doSomething()} />)

Since a function is an object, and objects get compared by reference, a new
function that does the same thing is not equal to the old function. Thus, this
is a change.

Negating it turns that into a boolean, which gets compared by value. false is
the same as false, so no change. Any type coercion (e.g turning it into a
string) would've worked.

In this case, there's nothing specific to React or hooks, just plain old JS
comparison.

------
chmln
The class-based examples given are a bit disingenuous.

Not only the author is not using anything like redux, the class examples look
deliberately made longer than they should be. For example, one of them has 4
setState() calls after each other instead of combining them. Another manually
performs deep equality checks for each key instead of using some
library/helper.

The conciseness of React hooks also relies on the knowledge of implicit
ordering of when certain functions are called, while lifecycle hooks are
obvious and straightforward. Optimizing for writability is not the best
approach for anything beyond prototype-level codebases.

~~~
vmind
I don't think it's fair to say React hooks are optimising for writability,
even if they are much shorter, but modularity. While lifecycle methods are
'obvious and straightforward', they often spread related functionality across
multiple different functions, which can be more easily grouped together with
hooks. And if you have similar lifecycle-based functionality that needs to be
shared between multiple components, you end up with a variety of not-great
solutions, whereas that is simple with hooks.

That's not to say hooks aren't without a downside, it _is_ more to learn, with
a different mental model, and their own edge cases. But having used hooks
exclusively for a larger project, I couldn't imagine going back to lifecycle
methods.

~~~
judofyr
All of these discussions seems to forget that React hooks is two different
things combined: (1) it's a new magic way of having stateful components and
(2) it's a new set of APIs. I don't quite see why they _have_ to be so
coupled. Wouldn't this be possible as well?

    
    
        class Chart extends Component {
          constructor(props) {
            super(props)
            this.useEffect(() => {
              const newData = getDataWithinRange(dateRange)
              this.setState({data: newData})
            }, () => this.props.dateRange);
          }
        }
    

You're not able to completely mirror the API (e.g. here the dependencies has
to be a function), but you would get code which behaves very similar without
the weird hook API. I'm sure these APIs would have various gotchas in order to
be used correct, but it's not like React hooks is gotcha-free at all.

Interestingly, useMemo doesn't even need to be defined in React in a class-
based component:

    
    
        function useMemo(f, dep) {
          let prev;
          let value;
          return function() {
            let next = dep();
            if (next !== prev) {
              prev = next;
              value = f();
            }
            return value;
          }
        }
    
        class Chart extends Component {
          data = useMemo(
            () => getDataWithinRange(this.props.dateRange),
            () => this.props.dateRange)
          )
        }
    

This is also just one of the possible APIs. I'm sure there are variants of
this which are more performant and/or easier to work with.

~~~
acemarke
Would it be _possible_ to implement hooks as part of classes? Sure, it's just
software, code can implement basically anything.

But, the React team deliberately opted to only implement hooks for function
components for a few reasons:

\- Function components didn't have capability parity with class components in
terms of having state and side effects

\- Class components already had the ability to do this kind of functionality
overall

\- Long-term, function components are easier for React to deal with for things
like hot reloading and the upcoming Concurrent Mode. By dangling a carrot to
convince folks to move to function components + hooks, it becomes easier to
implement that functionality for the React team.

------
Aeolun
I just don’t get hooks. People say it’s an easy way to keep your lifecycle
events close to the functionality they’re dealing with, but in the end it’s
just a clusterfuck of function calls in the render method.

As far as I’m concerned, dealing with all your lifecycle events in one or two
functions makes it much easier to reason about what is going on.

Because hooks look so much like magic, people treat them as magic and when
they don’t work as expected, they have no clue why (adding more hooks to solve
the problem!). I feel like Reacts’ surface area to shoot yourself in the foot
has increased fivefold since the introduction of hooks.

The only thing I found really compelling about hooks is the ability to write
shared functionality to include in multiple components, but practically I
still have to find the first time I actually need that.

~~~
ratww
> People say it’s an easy way to keep your lifecycle events close to the
> functionality they’re dealing with, but in the end it’s just a clusterfuck
> of function calls in the render method.

Maybe the code you work on is a special case, but in my experience that sounds
exactly like an opportunity to refactor your code into custom hooks, as you
mention in your last paragraph.

------
pgt
(Edit: it is worth noting that Hooks are based on Reagent's reactive atoms.)

Compare the complexity of Hooks with the simplicity of state management in
[Reagent]([http://reagent-project.github.io/](http://reagent-
project.github.io/)) or
[Rum]([https://github.com/tonsky/rum](https://github.com/tonsky/rum)).

Complete ClojureScript example using Reagent:

    
    
      (ns reagent-example.core
        (:require [reagent.core :as r])
    
      (def !count (atom 0)) ;; ! prefix is convention for state
    
      (defn counter-component []
        [:div
          [:p "You have clicked the button " @!count " times."]
          [:button {:on-click #(swap! !count inc)} "Increment"]])
    
      (r/render [counter-component] (js/document.getElementById "app"))
    

The same example using Rum:

    
    
      (ns rum-example.core
        (:require [rum.core :as rum])
    
      (def !count (atom 0))
    
      (rum/defc counter-component < rum/reactive []
        [:div
          [:p "You have clicked the button " (rum/react !count) " times."]
          [:button {:on-click #(swap! !count inc)} "Increment"]])
    
      (rum/mount (counter-component) (js/document.getElementById "app"))

~~~
ng12
I don't think this is a compelling example. React would implement this almost
identically:

    
    
        const Counter = () => {
          const [count, setCount] = React.useState(0)
          return (
            <div>
              <p>You have clicked the button {count} times.</p>
              <button onClick={() => setCount(count => count + 1)}>Increment</button>
            </div>
          )
        }
    

Replace the atom/swap with a useState hook and it's almost identical.

~~~
jmiskovic
The difference is in how the state is handled. In Clojure, it is contained in
an atom, which is a well documented language primitive which can change value
in controlled manner. It's not some magic construct hidden behind innocently
looking JS function useState().

~~~
dhucerbin
To be fair, reagent uses it's own implementation that satisfy atom protocol.
And it uses some funky magic with keeping derefs and triggering rerenders. In
principle, as atom is idiomatic Clojure, setState is idiomatic javascript.
ratoms and hooks both add some sugar to it.

~~~
pgt
`setState(...)` is not idiomatic in JavaScript.

~~~
dhucerbin
I'm not sure how far back you want to go, but `this.set` is used in most
frontend frameworks, starting around knockout and backbone. You had object
which managed state for you (then it was model, today it is component) and it
had some setter method.

------
the_gipsy
I prefer it when render functions are pure and just render. Given some input,
produce html that can dispatch some messages.

Deal with the state somewhere else. I don't want to have to reason about when
a component mounts, how many ways it could trigger things, that will cause
itself to render again and trigger more unexpected things.

Why did a component mount? Maybe the route changed, well I will just fetch
data where I handle that event. Maybe I got an API response, well guess what I
can do stuff right there.

All I had to do was let go of the idea of "component". I know, we need to make
layers, and isolate modules, separation of concerns. But "components" are
unsatisfactory for me, because all I ever got was implicit dependencies. Sure
this component deals with his own data, and its children do the same, but
somewhere, they DO have to talk to each other at same point, always. The
component depends on the user, or user's list of thingies, and bam I've
smeared it all across all of them. Because I'm building an application, not a
component catalog.

Also testing is a PITA. I think most of the time it's useless to test a
component without the whole app, and then it's an integration test, but you
really want an e2e test by then.

~~~
erikpukinskis
> I think most of the time it's useless to test a component without the whole
> app, and then it's an integration test, but you really want an e2e test by
> then.

I tend to agree. With a couple of caveats:

On my team we have visual tests that use factory data to just make sure
different states of each widget look right. I think it’s pretty valuable as a
safety net for CSS refactors. We use Chromatic for this, but they charge per
state, so even a medium sized app will get into the $500+ range per month.

I also think that, while in practice every front end I’ve worked on is tightly
coupled to the backend (making front-end only testing pretty useless), if you
build an “optimistic UI”, you can do it.

We got close to this at my last company, where we used Vuex to implement a
kind of a simple dB on the client, and allow server syncs to kind of build up
in the background as needed.

You still need to stub some initial requests, but you could test a UI by doing
that and then kind of “coasting” in the front end. Eventually you’d hit an
interaction that needed the server, but in this way you can test some short
sequences of interaction, which is probably enough to test the basic
interactions, and leave the E2E tests for actually making sure the front end
is wired to the server, db, work queues, etc correctly.

------
c-smile
> In a function component, we need to think about what values stay in-sync.

So we are removing Classes in favor of Functions.

Something tells me that on the next iteration we _will_ see something like
this:

"In a declarative component, we just need to declare what UI elements shall
stay in-sync with what items in data model."

And that is precisely what Angular is based on (declarative two way binding).

And the cycle of evolution spiral will be complete.

Life goes on ...

~~~
gombosg
Why? This is just slippery slope arguing. Two-way bound data is a huge leap in
framework complexity.

They require Zone.js stuff in Angular and use of ES2015 Proxies in Vue.js.
React has neither of these.

Hooks is basically a different syntax describing the same framework.

I personally love them since I no longer need to care about lifecycle methods,
but can describe state and state change logic.

------
omarhaneef
My favorite things about this post:

1\. The comparison of the lengths of the function using the two styles
(class/function)

2\. The comparison of the length of the _changes_ required in the two styles

3\. The clarity achieved by maintaining the two columns consistently distinct.

If you are interested in how to demonstrate the efficiency of a new coding
technique -- even if you're not interested in React Hooks -- this is worth a
read.

------
sbr464
One thing to watch out for currently are memory leaks that retain previous
state even if unmounted and/or unrelated.

I’ve seen several bugs especially with useRef/memo, and animation related
hooks, or hooks that combine several together to expose a simpler api.

I didn’t notice them until working with larger file uploads and local content.
They can be hard to spot with small changes, Since they are usually deep in
the memory trace stack.

The component with the bug doesn’t need to even be in the tree to accidentally
retain state that you’ve cleared/unmounted.

Since then I’ve made a simple test that saves a large test blob to local
state, and makes sure it gets garbage collected etc, not stuck in another
components previous state memory snapshot.

—

An example could be a loading animation above/outside the tree, that uses a
hook somewhere inside its library that tracks browser size changes. It
wouldn’t seem to affect you, but it could inadvertently cache some state in
the window. So you’d only detect it if you’re working with very large content.

------
daok
The class example is misleading in terms of the line of code. The first
example would have the componentDidMount and componentDidUpdate to call the
same function

    
    
      componentDidMount() {
        const newData = getDataWithinRange(this.props.dateRange)
        this.setState({data: newData})
      }
      componentDidUpdate(prevProps) {
        if (prevProps.dateRange != this.props.dateRange) {
          const newData = getDataWithinRange(this.props.dateRange)
          this.setState({data: newData})
        }
      }
    
    

Could be:

    
    
      componentDidMount() {
         get(this.props.dateRange);
      }
      componentDidUpdate(prevProps) {
        if (prevProps.dateRange != this.props.dateRange) {
            get(this.props.dateRange);
        }
      }
    
      get(dateRange){
        const newData = getDataWithinRange(dateRange)
        this.setState({data: newData})
      }
    
    

It removes the duplication.

~~~
TheTrotters
The original example has 10 LOC, yours has 12.

~~~
crtlaltdel
And the 12 LOC example allows you to change the logic in one place and not
two. While more verbose, this does make it harder to introduce bugs by
forgetting to update the logic everywhere it is used (copy/pasted)

~~~
stickfigure
Yes, it's a better pattern, but then the hookophobes would complain that the
example artificially inflates the line count by adding a not-strictly-
necessary function. I don't think the author is being disingenuous, just
trying to keep the examples straightforward.

It doesn't matter anyway. With or without the function, the class example
compares unfavorably to the hooks example.

~~~
daok
The hook functions need the knowledge that the mount and update are doing the
same thing. However, the example would be more complete if the mount was doing
something different. In that case, not only the class example would be more
clear to read with explicit function for mount and update but also would be
similar in terms of the line of code. With Hooks, if you want something
different you need to do a "trick" which is less obvious than defining
`componentDidMount`.

I am not against Hooks, but I do not believe the example portrait the
situation properly.

------
nojvek
Hooks are incredibly confusing because of the underlying dark magic. I’ve had
multiple people try to explain it to me but when I ask what does useState do
under the hood? Can you write a simple version of it, they kinda get stuck.

Initial react was super simple. A render function that renders a virtual dom
tree. It’s diffed against real tree. View is simply a function of state. You
could build a simple react in an hour and show the concepts.

New react feels doesn’t share the same ethos of radical simplicity.

~~~
thatswrong0
I’ve see this argument before but I don’t get it. How is this.setState any
simpler? Can you explain how class based components work in any detail? For
example, why do we need to call super(props) in the constructor? There’s a
whole Dan Abramov post about that one ([https://overreacted.io/why-do-we-
write-super-props/](https://overreacted.io/why-do-we-write-super-props/))...
is that really so straightforward?

Or are you just talking about “pure” functional components? How would you
build a full featured application with just those?

I think it’s actually _less_ magical now, because of the required static order
of hook invocation. This implies that each component instance has something
like an array backing it that keeps track of hooks calls between renders. This
array maybe could hold said state value at the index corresponding to the
static ordering of that useState call. So then maybe useState just populates
the initial value in the array and provides a callback which both sets the
value in the array and then queues up a component re-render

------
valgaze
What an excellent & incisive writeup

Also worth flagging this great touch-- scroll-aware "scribble"/cross-out
animation: [https://imgur.com/a/mhPCjNy](https://imgur.com/a/mhPCjNy)

------
fpgaminer
In the few cases I've used them, I've liked Hooks. I just have one big pet
peeve with them. Everyone seems to write hooks in Least Significant Function
First coding style. This is where you define dependent functions before the
code/functions that call them. A short example:

    
    
      function Hello() {
        // other stuff
      
        const callback1 = () => {
          // do stuff
        }
      
        // other callbacks, effects, etc
      
        return <button onClick={callback1}>{label}</button>;
      }
    

See how the callback functions, and everything else, is defined before the
"rendering" part of the function? Compared to, say:

    
    
      function Hello() {
        const render = () => <button onClick={callback1}>{label}</button>;
    
        const callback1 = () => {
          // do stuff
        }
      
        // other callbacks, effects, etc
      
        return render();
      }
    

That uses the Most Significant Function First approach, where the higher-order
function (render) is defined first, and the functions it depends on are
defined after them.

That latter example, AFAIK, compiles fine (filling in the elided etcs, of
course).

To me, MSFF reads better. The higher order function being defined first gives
the reader a lay of the land. They provide the highest overview of what the
module as a whole does. The functions that follow drill down into increasingly
finer details.

LSFF is backwards, forcing the reader to understand the finer details of a
module without having any clue about their context or the overarching goal
until the very end of the code.

React specifically, as a reader, I want to know first "What are we rendering?"
Only then do I want to then know where the data that goes into that render
comes from, the processing of that data, and what user interactions there may
be.

I see LSFF a lot, especially in React code. Maybe it's just a personal
preference, but I just feel like most coders are jumping straight to the
"render" part of the code anyway, before looking at the rest of the module. So
why write it at the bottom? Why not write the code the way it should be
understood?

~~~
ArchReaper
I'm not sure I follow your question. It's Javascript. Code goes from top to
bottom. You can't write:

    
    
        function doThing(target) {
          console.log(myVar);
          const myVar = target.prop;
        }
    

Because that's invalid Javascript. React Hooks are just functions that return
markup.

If your goal is simply to see what it renders first, that's fine, you simply
need to look at what the function returns.

I disagree with your assertion that LSFF is 'backwards' \- it is simply not
what you are used to.

MSFF could definitely be argued for class-based components, but it's simply
not an option for hook-based components.

~~~
Izkata
They didn't write that, they wrote this:

    
    
      function doThing(target) {
        function run() { console.log(myVar); }
        const myVar = target.prop;
        run();
      }
    

The console.log's evaluation is delayed, so it's totally valid.

------
artyomavanesov
The pace at which React is evolving is both exciting and frustrating. There is
so much you can do with all these new tools, but your code is never really up
to date.

I picked up web development 1.5 year ago and already went through several
stages of re-learning. It's great that the ecosystem is developing this
rapidly. But it does require significant effort to stay on top of the latest
techniques. Especially if you're not a full-time developer.

~~~
ratww
I wouldn't say that the pace is too fast, it's just that you started at the
right time :) Hooks are by far the largest change ever in React, but that
happened after many years of stability.

------
rebotfc
The thing I like about hooks is that components can be truly modular in terms
of both behaviour and state. They can so be relatively easy to reason about in
isolation and therefore it's easier to reuse them with less plumbing needed.

For instance a simple custom hook use_graphql_list(url,query) can encapsulate
the entirety of fetching json via a graphql query keeping an array up to date
and in sync and present data in a format that is easy to use in a list
component.

For instance in rust:

    
    
      let graphql_query = "{countries {name currency emoji}}";
      let graphql_url = "https://countries.trevorblades.com/";
      let container_name = "countries";
      let (list, graphql_control) =
        use_graphql_list::<Country>(
          graphql_query,
          graphql_url,
          container_name);

------
gimboland
Not a comment on the content, but I really wish people wouldn't publish blogs
without an obvious publication date.

I learnt react this year; I spent quite a bit of time researching and reading,
and obviously there was a _lot_ of ground to cover. In that process, it was
really helpful to know whether what I was reading was published a month ago or
three years ago.

The first words on that page (after the title) are "React introduced hooks one
year ago". That may be true today; it won't be in a year, but in a year
somebody looking at this page will probably see exactly what I'm looking at
now. Not showing a publication date does that somebody a disservice.

</moan>

------
lewisflude
From this thread it seems a lot of people aren't very familiar with React
Hooks yet. I highly recommend diving into it. useEffect can replicate
lifecycle methods in a way that's much more sensible and easy to read if done
right (in my experience of course).

------
bg0
This is off topic but can anyone tell me what color theme this code is in? I
need this for VSCode

~~~
bbx
Whenever I like a syntax highlighter theme on a website, I search on Github a
few of the hex codes. Like this:
[https://github.com/search?q=65ddd9+ff767b+ffc312&type=Code](https://github.com/search?q=65ddd9+ff767b+ffc312&type=Code)

In this case, there's only 1 result… the author's public repo. So I guess the
search worked! But the theme seems to be a personalised one the author created
themselves.

But the primary colors remind of Solarized Dark, if that helps:
[https://www.google.com/search?q=solarized+dark&tbm=isch](https://www.google.com/search?q=solarized+dark&tbm=isch)

~~~
bg0
Very clever. Thanks man!

------
istoica
Is there a way to trigger something after state changes ? In the class based
components, setState has a second argument that gets triggered after state has
updated.

This is nowhere to be found in the hooks approach, without using flags and
other strange constructs.

Where is it gone ?

~~~
gherkinnn
Use useEffect.

const [thing, setThing] = useState(true)

useEffect(() => { console.log({ thing }) }, [thing])

~~~
rpastuszak
Not that it matters too much given the question asked, but am I right to think
that the useEffect callback would get fired on the next render (function call)
here?

~~~
lilactown
The function given to useEffect will be fired after a successful _commit_ (aka
React actually manipulates the DOM after a render).

Here's a helpful diagram that shows the difference between render and commit
phases in terms of the class lifecycle methods:
[http://projects.wojtekmaj.pl/react-lifecycle-methods-
diagram...](http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/)

So when a render executes useEffect with a callback (and values in its
dependency array has changed), it will schedule the callback to be run once
React has committed the DOM changes based on the current render.

------
mikebannister
Excellent tutorial for person familiar with React lifecycle learning basics of
hooks... but mostly came to comment on the exceptionally high quality of the
teaching and format of the tutorial. I especially love the "diff view".

------
steve_taylor
Try creating a hooks-based component that increments an internal counter every
second, using useState to track the counter and setInterval inside useEffect
to start the timer on mount and stop it before unmounting.

Good luck with that.

~~~
foolbiter

      function Counter() {
        const [counter, setCounter] = React.useState(0);
        React.useEffect(() => {
          const interval = setInterval(() => setCounter(n => n + 1), 100);
          return () => clearInterval(interval);
        }, [])
    
        return (
          <div>{counter}</div>
        );
      }

~~~
steve_taylor
I had no idea the setter can take a function instead of a value. It makes
perfect sense, because setState has the same feature. TIL.

~~~
acemarke
FWIW, it's documented in the API reference:

[https://reactjs.org/docs/hooks-reference.html#functional-
upd...](https://reactjs.org/docs/hooks-reference.html#functional-updates)

------
imtringued
This looks like dataflow programming to me and the big downside are the so
called "glitches". We want to react to every change but at the same time we
want some changes to happen simultaneously.

    
    
          const data = useMemo(() => (
            expensive(a,b,c)
          ), [a,b,c])
    

Now image a,b,c all change at the same time in response to a button press. How
often is expensive() going to be called? 3 times? That will take too long. 1
time? That's unpredictable.

~~~
acemarke
React batches updates that were queued from within its own event handlers. So,
if you had:

    
    
        setA(1)
        setB(2)
        setC(3)
    

in a single click handler, there would be a single render, and a single call
to the `useMemo()` callback.

On the other hand, if the state setters were called outside of a React event
handler (such as a `setTimeout` or somewhere in an async function), each of
those would cause a separate render pass, and the `useMemo` callback would end
up being called 3 times.

Note that in the upcoming Concurrent Mode, React will batch updates across
event ticks by default.

------
_hardwaregeek
Hmm, how do you allow for more complicated updating schemes? Like if I want to
update on something other than a shallow comparison? Correct me if I'm wrong,
but the dependency array seems to use shallow comparisons.

Also, and this is a totally minor quibble, but I hate it when websites have
that tiny bit of horizontal scroll. The animations for code size were very
pretty though.

------
mmcnl
I like React Hooks, I like the simplicity. I dislike that it makes reading the
code much more difficult. The code required to do a simple
"componentDidMount()" is very abstract. It is difficult to reason about what a
component does at first glance, with the class methods this is much easier.

~~~
ArchReaper
To do 'componentDidMount' you simply need a 'useEffect' with an empty (non-
null) dependency array. It makes sense to me - maybe the issue is more
familiarity than simplicity?

Edit: useEffect performs a side effect every time one of it's dependencies has
changed value. Passing an empty array means the effect is only run once (on
mount) because there are no dependencies.

------
mharroun
The this.setstate spamming and lack of using something like the PureComponent
class show that the author either is bad at coding in class based react or
intentionally making it look worse.

Disclaimer: got nothing against hooks but hate false examples.

------
ummonk
This article was extremely difficult for me to follow, even though I already
have experience with React Hooks and really like them.

------
jpincheira
Wow what a sick animated page! Amazingly done.

------
blairanderson
The formatting and UX of this article were jawdropping. good job!

------
pier25
Great article, but to me hooks solve problems that I rarely have.

Anyway I left React/Preact/Inferno/Vue for Mithril and my life is a lot
simpler now.

------
smrq
Obligatory HN post not about the contents of the post:

On my MacBook, fullscreen, I get horizontal scrolling on this page. Honestly,
what kind of monitor was this site designed for?

The content is excellent, though.

~~~
Waterluvian
It worked beautifully on my stupid loaner 1080p screen.

