React 18, useEffect is getting called two times on mount

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 React 18, 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.

Leave a Comment