reactjsreact-hooks

useUpdateEffect is not working in React 18


I need to call an effect callback whenever a particular state is changed inside my component but the callback should not be called during the mounting stage. I have created a custom hook (Refer).

function useUpdateOnlyEffect(callback) {
  const componentJustMounted = useRef(true);

  useEffect(() => {
    if (!componentJustMounted.current) {
      callback();
    }
    componentJustMounted.current = false;
  }, [callback]);
}

I've created an example in codesandbox and the code works fine in React 17 but it is not working in React 18, i.e the effect callback is called during mounting as well. I've checked the changel og of react 18 but couldn't find the solution. Is this happening due to Automatic Batching introduced in React 18?

This code will work fine in production mode or by removing strict mode. The reason is React team is planning to remove/add sections of UI while preserving the state of the component before unmounting in the future.

With Strict Mode starting in React 18, whenever a component mounts in development, React will simulate immediately unmounting and remounting the component:

This is just to ensure our component is resilient to reusability in future.

To know more, refer Reusable state and follow the github discussions here.

Anyway, if you add additional effect(not sure if it is recommended) to reset the flag(ref) value the hook will work perfectly fine in dev build as well.

useEffect(() => {
  return () => {
    componentJustMounted.current = true;
  };
}, []);

Solution

  • I think it is related to the Strict Mode. See the update here: https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors

    To help surface these issues, 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.

    So your component is mounted twice in dev mode, on second load, your flag is false.