
A guide to useEffect in React - feross
https://overreacted.io/a-complete-guide-to-useeffect/
======
jameslk
React hooks solve a problem most people don't have. Classes worked fine in 99%
cases and have the nice benefit of encapsulating any side effects. Hooks make
it easy to hide side effects and dependence on state (think global variables)
by letting developers hide these things several layers deep in functions that
may get called by components. This is a footgun waiting to go off.

If you need to use state or side effects, just use classes. Most of the time
all you need is pure components with fewer stateful components above
controlling the pure components. Pass down your state and state modifiers
through explicit and easy to follow props (or use a state management system if
you find yourself prop drilling). If you're using state and side effects
everywhere, you're probably not using React correctly.

~~~
littlecranky67
When you use TypeScript with React (and want sound typings per policy so any
type erasures/widenings are considered bad practice) Hooks are a big leap
forward.

Typing classes was much to verbose, and the type system of TypeScript to this
day is not a perfect fit to type class-based components. Hooks solve this
problem - probably unintentionaly.

Example: With Hooks you almost never need to explicitly type the type of state
of a component, as whatever you pass to useState() will auto-infer the type.
While when using class-based Components, you'd have to specify the state type
as a generic argument; you'd have to manually specify it when you set a state
using this.setState() as partial types were allowed due to reacts auto-merging
of the passed argument to the state. And you'd have to use some kind of
Partial<State> type when assigning only some values of the state in the
property constructor.

Similar issues exist when using the static defaultProps field in TypeScript -
the type couldn't be inferred from the generic class argument, as a
Partial<Props> type is valid for the defaultProps field. As a result, the
defaultProps (as well as the default state property) is cast as the empty
object type {} by default if you don't explicitly assign a type - which makes
you lose basically all the type information because every object is assignable
to {} in TS.

And if you ever tried to strictly type your HOCs or renderProps you know how
hard it was, sometimes utilizing the conditional/mapped generic types feature
of TS. This is completely gone with Hook.

I could go on telling how stupid it is when you disable nullable types in TS
(strict mode) because TS will not let you use any non-nullable prop inside of
render() - even if that prop is initialized in the static defaultProps field.
You'd have to override the non-null check using TS exclamation point operator.
This is also gone with Hooks.

I don't know if they designed the Hooks API with TS in mind or if this is
completely accidental, but if you migrate a TS project with strict mode
enabled to Hooks, you will see how much implicit type annotations can be
dropped, and still retain strict type checks. Big fan of it.

~~~
ponyous
I'm writing TS components daily, I'm not sure why, but I never encountered any
of the issues you mentioned.

I can only agree that typing HOC was hard when I did it the first time.

~~~
littlecranky67
Then let me ask you: How do you type .defaultProps? How do you type the
"state" property on the class? If you _did_ explicitly repeat the type already
specified in the Generic Type Argument: Component<IProps, IState> you would
know what I mean with too verbose (you have to repeat a type that could be
infered, but due to TS restrictions isn't able to). If you _did not_ repeat
the type for those fields, you lost typesafety without noticing it. You can
then go ahead and assign { foo: "bar" } to state (or use it in setState) and
will not get a compilation error (which you shold as foo is not a known
property for your state).

And I sure as hell bet you do not use "strict" compilation in TS, which opens
typing issues everywhere.

~~~
ponyous
We are using strict mode.

If I do `setState({ undefinedStateKey: 123});` it warns me. Just checked. It
may be linting not TypeScript. We have IState defined only as Generic Type
Argument.

We define defaultProps as static in class. And you are right defaultProps are
not being type-checked against Generic Type Argument IProps.

~~~
littlecranky67
Well if you are in strict mode, it should break the build with an error, not a
warning. This seems to be a linting rule.

But you see the issue with defaultProps and state here. You can basically
assign anything to defaultProps or use in setState() even so you were explicit
of the props type in the Generic Type Argument.

------
bgdam
Having used React hooks for the past couple of months, I think I've come to
the conclusion that I'm not going to recommend any body to switch away from
class components.

I understand that hooks solve the problem of being able to compose side-
effects and lifecycle in your components, but honestly, in the last three
years of using React, I don't think I've ever actually had a scenario where
hooks would have been a great solution for me. I'm not sure if this means I've
been designing my React components very well, or very badly or what.

While hooks look like they might be good at some things (ex: useState is
awesome), I think they come with too many subtle gotchas (the amount of
literature on handling these gotchas, that's been written in the few months of
hooks being in beta is mind-blowing) that I'd much rather just use class
components.

~~~
ryansolid
That's ultimately it isn't it. I think Hooks are great and I've been using
them and would use them for everything. I'm also a Dev manager, and
understanding those gotcha's it isn't even about not knowing which would be an
obstacle for less experienced developers, but how easily you repeatedly
accidentally fall into them.

It's hard. I feel like they are 90% right, but unless you are not only a very
competent developer but also insanely careful you will make many mistakes.

I perhaps am a bit more torn as I have experience with fine grained libraries
like Knockout and MobX and I even more so see the benefit of the potential
here. I think the idea of it is a better, more clear way to solve problems
(from previous experience). But the number of times I've missed [], or forgot
to close over state properly even knowing. I usually catch myself but I can
only picture the team biting into this.

------
dsenkus
I rewrote my side project ([https://robojs.com](https://robojs.com)) from
Typescript/Redux/React (class based components) to Typescript/Mobx/React
(hooks) in less than a week. The process was really simple (mostly deleting
code). In the end the source code is so much smaller (at least 50%), simpler,
easier to read and reason about, largely because of Mobx, however hooks is so
much nicer to work with compared to class components, and I doubt that I would
ever want to go back.

------
ng12
> While you can useEffect(fn, []), it’s not an exact equivalent. Unlike
> componentDidMount, it will capture props and state. So even inside the
> callbacks, you’ll see the initial props and state.

In my experience this is a fairly common source of confusion. People have a
tendency to write things like:

    
    
       useEffect(() => {
          const unsub = subscribeToThing(thing => handleThing(props.id, thing))
          return () => unsub()
       }, [])
    

This looks pretty good except it's broken if props.id changes. handleThing()
will always be called with the first props.id.

I'm the defacto React tutor at my company and issues like this make me pause
before recommending new React devs to use hooks. useState is awesome and feels
more natural to people than setState(). useEffect seems similarly simple but
the subtleties can catch you.

~~~
sophiebits
The lint rule Dan mentions (eslint-plugin-react-hooks/exhaustive-deps) will
catch errors like this.

It’s also worth noting that most class components people write don’t handle
all prop updates correctly – Hooks tend to be slightly harder at first but
tend to have fewer pitfalls in edge cases, especially if you stick to the
rules (which there are lint rules to enforce).

~~~
snek
But there's something to be said about needing a lint rule for the API to be
safely used.

~~~
blankaccount
The nice thing about functional programming is that you _can_ reason about
execution and write lint rules to fix pitfalls that you can't detect with
imperative programming.

------
kaoD
I've toyed with hooks for a few hours and, even if I understand why they're
focusing on teaching the basic pitfalls (dependencies arrays mostly), I don't
think they're the most confusing part (as long as you know how closures work,
i.e. as long as you know JS).

The real deal is `useEffect` vs `useLayoutEffect`. It seems easy, but it's
not. When should I use each? According to the docs, use `Layout` if you
interact with the DOM for measurement or whatever (I mean, look at its
name)... WRONG!

I made a custom `useFetch` hook that uses `useEffect` to re-fetch when the URL
changes. It should be fine, right? Every other `useFetch` I've seen on GitHub
does that too... And it's wrong.

I noticed that as soon as I added a second `useFetch` that fetches depending
on the first's data, there is flashing in the UI. Since `useEffect` is
asynchronous, there's a small gap where both internal states can be
desynchronized (completely unrelated to the promise, I mean the internal
`isLoading`, etc.) `useLayoutEffect` solves the issue (since it blocks the
rendering until both effects are finished updating their internal states) but
then... my internal hook implementation affects the external behavior in non-
obvious ways!

I don't know whether my hook consumer needs to block the UI until all updates
finish, so I actually need to make two different `useFetch` and `useSyncFetch`
hooks, or just ditch the async one.

It's definitely a footgun. I predict this is going to bite everyone's ass.
It's also not really well understood (try searching for "useEffect vs
useLayoutEffect") and the React team is completely ignoring the issue. I
haven't seen it mentioned anywhere, and I can't believe I'm the only one that
noticed it (I guess hooks haven't been used that much yet?)

The issue is exacerbated by `useEffect` being the "default" in the docs, while
`useLayoutEffect` is explained later as an option (even if they admit that
it's the safest option if you're migrating from classes).

I recommend sticking to `useLayoutEffect` unless you're absolutely 100% sure
your consumer does not need to block the UI (when can you be 100% sure?)

There's a similar thing with `useState` vs `useRef` for mutable state (where
the only difference is that `useState` triggers re-renders) but since
`useState` is presented as the default (re-render even if you don't know if
for consumer needs to) it does not seem like that much of a footgun (you have
to reach for `useRef` if you really need it, e.g. in the infamous
`useInterval` Abramov's blog post). It's also very similar to how state vs.
instance variables worked, and it's very obvious how `useState` interacts with
React (completely different behavior unlike `useLayoutEffect`, which is the
same but in different parts of the component lifecycle).

When your internal hook implementation affects your component's behavior in
non-obvious ways, there's something wrong with the hooks API. As proof, see
how many custom `useFetch` hooks use `useLayoutEffect` when they should. Zero.
And I'm sure there are many more instances of this problem hidden in other
custom hooks (it's hard to tell, since it's not obvious until you use a hook
that depends on other hook's output and see the flashing).

~~~
danabramov
Do you want to file an issue? I’d be happy to answer some of these questions
and maybe improve the docs.

useLayoutEffect is not the recommended way to do it. You’re probably missing a
simpler solution like setState during render. But I’d need to see the code to
be sure, and GitHub issue would be a better place to do it.

~~~
kaoD
Definitely, it crossed my mind, I just have not had enough time to write an
minimal bug example unfortunately :/

In this case I think the problem is `setState` is inside my `useEffect` and
I'm using its dependencies array as a sort of substitute to
`componentWillUpdate` + compare `props` to `prevProps` (hence the async state
update).

In class-based react I would just atomically `setState` both states and there
would be no problem, but since hooks allow you to modularize behavior a new
type of data dependency appears (there are multiple states in a single
component) making it harder to reason.

Thanks for chiming in, will file an issue ASAP.

~~~
danabramov
No problem, I'm sorry for the confusion. I think your use case might be solved
by this approach:

[https://reactjs.org/docs/hooks-faq.html#how-do-i-
implement-g...](https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-
getderivedstatefromprops)

------
lewisl9029
I haven't used this pattern very extensively yet myself, so definitely take
this with a grain of salt, but I think a good way to avoid the pitfalls around
accidentally omitting depencencies consistently might be to define our effect
functions as pure functions _outside the scope of the render function_, that
takes a list of dependencies from our render function, and then returns an
effectful function to pass into useEffect.

Since these functions don't even have access to the render function's closure
to begin with, we can eliminate the possibility of our effect functions ever
lying about their dependencies, as they have no choice but to receive their
dependencies explicitly as arguments.

Probably easier to demonstrate with a code sample:

```

    
    
      const effectFn = (dependency1, dependency2) => () => console.log(dependency1, dependency2)
    
      const Component = ({prop1, prop2}) => {
        const dependencies = [prop1, prop2]
        useEffect(effectFn(...dependencies), dependencies)
    
        // rest of render function...
      }
    

```

We can even define a separate hook to solidify this pattern and make it less
cumbersome to write. I whipped up this quick example a while ago to show
coworkers and tentatively named it `useEffectFromArgs`:

[https://codesandbox.io/s/4wwqnqp3zx](https://codesandbox.io/s/4wwqnqp3zx)

For those who don't want to click the link, it's just a really thin wrapper
around useEffect:

```

    
    
      const useEffectFromArgs = (effectFromArgs, args) => 
        useEffect(effectFromArgs(...args), args)
    
      const effectFn = (dependency1, dependency2) => () => console.log(dependency1, dependency2)
    
      const Component = ({prop1, prop2}) => {
        useEffectFromArgs(effectFn, [prop1, prop2])
        // rest of render function...
      }
    

```

I actually came up with this originally because I wanted a way to be able to
test effect functions in isolation as pure functions rather than involve the
entire React rendering pipeline just to test something that could have been
tested as a pure function, which are by far the simplest tests to write and
reason about, and the fastest tests to run. So for me, avoiding the accidental
scope capture was really just a nice side effect.

The pattern is equally applicable to other hooks like useCallback, which was
actually the original motivation for this. I also have another version of this
that use named arguments to specify dependencies rather than a list, that I
prefer using over the other one because positional semantics don't scale:
[https://codesandbox.io/s/4x86wy3r60](https://codesandbox.io/s/4x86wy3r60)

It does make me wonder why the hooks API doesn't just work like this to begin
with though. I could definitely be missing some downsides to this approach, so
if anyone wants to chime in with any critiques/insights, I'd love to hear
them.

------
miccah
Maybe I'm not the target audience, but I have no clue what "useEffect" does or
why it is important. An introduction on the subject (including which language
is being discussed) would be really helpful.

~~~
uhryks
There's a link in the first sentence of the article. It would have taken you
less time reading it than it probably took you to write this comment.

~~~
lkbr
Also "This article assumes that you’re somewhat familiar with useEffect API."
with another link a little further down.

