-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Performance issues when many <Menu> elements exist on one page #3630
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
Comments
Hi, any change to merge the fix by @rkoval? |
RobinMalfait
added a commit
that referenced
this issue
May 10, 2025
This PR fixes a performance issue where all components using the `useIsTopLayer` hook will re-render when the hook changes. For context, the internal hook is used to know which component is the top most component. This is important in a situation like this: ``` <Dialog> <Menu /> </Dialog> ``` If the Menu inside the Dialog is open, it is considered the top most component. Clicking outside of the Menu or pressing escape should only close the Menu and not the Dialog. This behavior is similar to the native `#top-layer` you see when using native dialogs for example. The issue however is that the `useIsTopLayer` subscribes to an external store which is shared across all components. This means that when the store changes, all components using the hook will re-render. To make things worse, since we can't use these hooks unconditionally, they will all be subscribed to the store even if the Menu component(s) are not open. To solve this, we will use a new state machine and use the `useMachine` hook. This internally uses a `useSyncExternalStoreWithSelector` to subscribe to the store. This means that the component will only re-render if the state computed by the selector changes. This now means that at most 2 components will re-render when the store changes: 1. The component that _was_ in the top most position 2. The component that is going to be in the top most position Fixes: #3630 Closes: #3662 # Test plan Behavior before: notice how all Menu components re-render: https://github.com/user-attachments/assets/3172b632-0fa4-42db-970c-39efc827dd84 After this change, only the Menu that was opened / closed will re-render: https://github.com/user-attachments/assets/5d254bfc-5233-47a7-94d3-eb7a8593e14f
Hey! Went with a different approach to solve this issue. This should be fixed by #3722, and will be available in the next release. You can already try it using:
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What package within Headless UI are you using?
@headlessui/react
What version of that package are you using?
v2.2.0
What browser are you using?
Firefox (but happens on any browser)
Reproduction URL
Describe your issue
As of
@headlessui/react v2.1.0
, opening / closing a Menu causes all other Menus on the same page to re-render, resulting in noticeable lag. To reproduce, visit the above sandbox and attempt to open a menu on any row.Thank you!
The text was updated successfully, but these errors were encountered: