The tricky behavior of useEffect hook in React 18

kirill ibrahim
Geek Culture
Published in
4 min readMay 20, 2022
Banner Imag eThe tricky behavior of useEffect hook in React 18

React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.

The React team is paving a way for a new feature that will be added to react in the future, that allows React to add and remove sections of the UI while preserving the state. As it requires components to be resilient to effects being mounted and destroyed multiple times. You can read more about New Strict Mode Behaviors in React 18, and why react team adds them:

Please follow me over Medium, to get a notification about next new articles. I’m also active on Twitter @IbraKirill.

The useEffect callback in this case runs twice for the initial render. After state change, the component renders twice, but the effect should run once.

Example:

useEffect(() => {  
console.log("You will see this log twice for dev mode, once after state change - double effect call.")
}, []);

The output:

You will see this log twice for dev mode, once after state change - double effect call.You will see this log twice for dev mode, once after state change - double effect call.

With Strict Mode in React 18, Effects fire twice in <React.StrictMode /> was added in React 18, and will simulate unmounting and remounting the component in development mode, So each component is mounted, then un-mounted, and finally re-mounted again:

Before React 18, React would mount the component and create the effects:

* React mounts the component.
* Layout effects are created.
* Effect effects are created.

With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:

* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effect setup code runs
* Effect setup code runs

I can imagine people being surprised by this strange behavior, Especially this not being mentioned/referenced in the useEffect docs here: https://reactjs.org/docs/hooks-effect.html

Also, I see no mention of this breaking change in the changelog: https://github.com/facebook/react/blob/main/CHANGELOG.md#1800-march-29-2022

It is described in the upgrade post & in the Foundational Update React 18.

The worst problem will be gonna face, when we fetch data from API inside useEffect, Then we have questions about where to put fetch calls so that they are not triggered twice in dev mode useEffect callback?

I found solution for this issues in one of the Github issues about React 18 from Dan gaearon:

There is an advice is not to fetch from useEffect at all. There are many reasons not to (fetch on render leads to waterfalls, you start fetching too late which is inefficient, you don't have a good place to cache the result between components, there is no deduplication between requests, etc). So I'd recommend a library like React Query or similar instead.

If you fetch from effects, you can do something like this:

useEffect(() => {
let ignore = false;
fetchStuff().then(res => {
if (!ignore) setResult(res)
})
return () => { ignore = true }
}, [])

This will not prevent the double-fetch, but it will ignore the result of the first one. So it’s like it never happened. There is no harm from the extra fetch call in development. You could also use a fetching helper with cancellation, and cancel the fetch instead of ignoring its result. Keep in mind that in production you’ll only get one fetch.

This is exactly what <StrictMode> is doing though, isn't it? If you don't want it - don't wrap your app in StrictMode, but I assume that the StrictMode does more than that. And it wasn't doing that in React 17. I don't argue - I'm just curious why this specific breaking behaviour couldn't have been added as opt-in. I assume there are good reasons behind that.

One of funny comment said in the Github issues about React 18 from @cytrowski:

There are recruitment coding assignments from the category: “show me you can fetch some data from the REST API”. I can imagine both the recruiter and the candidate being surprised why the fetch is being called twice (assuming they work with React 17 projects right now and used CRA just for the sake of the live coding task).

I think in my opinion, I will just stick to React 17 for a while longer, until React team fixes this issue.

I hope I added value, If you enjoy reading the article and want to support me as a writer, you can buy me a coffee!

If you want to Dive into React 18 with practical examples, I advise you with the following course.

If you want to Dive into Best Practices Patterns in React. I advise you with the following Course.

If you want to Dive into Server Side Rendering with React, I advise you with the following course.

--

--

kirill ibrahim
Geek Culture

I am a Web Front-End Engineer with 8 years of commercial experience working in various web-related roles. https://kirillibrahim.work/ My Twitter: @IbraKirill