Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call beforeEnter twice by <Transition> on React 18 & Vite #1370

Closed
ycs77 opened this issue Apr 27, 2022 · 1 comment
Closed

Call beforeEnter twice by <Transition> on React 18 & Vite #1370

ycs77 opened this issue Apr 27, 2022 · 1 comment

Comments

@ycs77
Copy link

ycs77 commented Apr 27, 2022

What package within Headless UI are you using?

@headlessui/react

What version of that package are you using?

  • @headlessui/react: v1.6.0
  • react: 18.1.0

What browser are you using?

Chrome

Reproduction URL

Open codesandbox and click button, see the console log...

Describe your issue

Call beforeEnter twice by <Transition> on React 18 & Vite.

I've seen the issue #311 and a PR #1183 resolved it, but @headlessui/react v1.6.0 still has this bug... 😅

@RobinMalfait
Copy link
Collaborator

Hey! Thank you for your bug report!
Much appreciated! 🙏

As far as I can tell, this is not really a bug, but a side effect due to StrictMode. The fixes applied in #1183 were an issue due to SSR and that issue got fixed.

The reason the callback is being called in Vite & React 18 is because StrictMode is enabled, which will mount/unmount your component twice in development to prepare yourself for ConcurrentMode. You will notice that in the blog post they mention this:

* React mounts the component.
    * Layout effects are created.
    * Effect effects are created.
* React simulates effects being destroyed on a mounted component.
    * Layout effects are destroyed.
    * Effects are destroyed.
* React simulates effects being re-created on a mounted component.
    * Layout effects are created
    * Effect setup code runs

You can also test that yourself by providing this useEffect:

useEffect(() => {
  console.log("executing effect");
  return () => console.log("cleanup");
}, []);

You will see the following logs:

  • executing effect
  • cleanup
  • executing effect

The reason this works in Vite + React 17 is because React 17 doesn't have this StrictMode behaviour. The reason this works in Next.js & React 18 is because StrictMode is not enabled in that CodeSandbox.

You can easily test this by adding a console.log in the render function:

export default function App() {
  console.log("Render");
  const [show, setShow] = useState(false);

  const beforeEnter = () => {
    console.log("call beforeEnter ...");
  };

  return (
    <>
      <Popover>
        <Popover.Button onClick={() => setShow(!show)}>Button</Popover.Button>
        <Transition show={show} beforeEnter={beforeEnter}>
          <Popover.Panel static>content...</Popover.Panel>
        </Transition>
      </Popover>
    </>
  );
}

You will notice that in Vite & React 18 you will get 2 logs for Render, but in the others you will only get 1, indicating that StrictMode is not running.

While this is a bit annoying in development, I hope this makes sense since we are following the rules of not making assumptions and doing the proper execution of effects & cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants