This behaviour is from React 18
itself when you are in development
with Strict Mode
. The core team is trying to implement this feature where we can preserve components’ state
even after unmount
. useEffect
getting called twice is related to this feature. Here is an overview of what they say in the doc:
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state.
With
Strict Mode
starting in React18
, whenever a component mounts in development, React will simulate immediately unmounting and remounting the component.
On the second mount, React will restore the state from the first mount. This feature simulates user behavior such as a user tabbing away from a screen and back, ensuring that code will properly handle state restoration.
⚠️ This only applies to
development
mode,production
behavior is unchanged.
However if you need to, for example in your case where you want the useEffect
‘s callback to execute only when count
changes, you can use a boolean ref
using useRef
to add some additional controls, like so:
import { useState, useEffect, useRef } from "react";
const Counter = () => {
const firstRenderRef = useRef(true);
const [count, setCount] = useState(5);
useEffect(() => {
if(firstRenderRef.current){
firstRenderRef.current = false;
return;
}
console.log("rendered", count);
}, [count]);
return (
<div>
<h1> Counter </h1>
<div> {count} </div>
<button onClick={() => setCount( count + 1 )}> click to increase </button>
</div>
);
};
export default Counter;
The above code will log only when count
changes in production
. And in development
with Strict Mode
, you would get one
log on mount
and not two
as you had before because of that if
statement.