-
Notifications
You must be signed in to change notification settings - Fork 834
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
Wrap custom events dispatching with flushSync
#1292
Conversation
Hi @Andarist, Thanks for this. I am not aware of
I will look into the original issue a bit more too. |
You can check out the Twitter thread where I've linked to this PR: https://twitter.com/AndaristRake/status/1509537858423566344 This might have some additional interesting info in it. At no point, any React team member has suggested there that this is the wrong solution. I understand why you might be worried about this here but I would roughly classify this as equivalent to an inferred default priority for all discrete events. It just happens that you are using custom events and thus the builtin detection in React can't classify this automatically as triggered by the user. Note that there might be some subtle differences as this one really flushes the whole update synchronously whereas maybe for discrete events things can be batched in a better way. I think both would flush in the same synchronous frame - but perhaps with Either way - I think it's worth landing this ASAP and researching the performance implications separately as this should fix bugs for people that are hard to diagnose and I don't think it should come with a severe performance cost, maybe just with a minor one (if at all). @jjenzz might also have some context for this function as I remember she was asking about it in the React repo quite some time ago |
Thanks for the added details and the link to the tweet. It might make sense to apply the same change to other places we use custom events (a quick search shows 3 files: One thing I am unclear about, is whether this only applies to custom events created using the I appreciate your help on this 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will also need to add react-dom
as a peer dependency of any package that now uses it and also to any of the packages that depend on one of those.
Wrapped all of them.
I believe that this would apply to
I've added the peer dep to dismissable-layer, toast and tooltip already had it. I've also checked all dismissable-layer dependants and all of them had a peer dep on react-dom. |
onOpen(); | ||
} | ||
onOpenChange?.(open); | ||
ReactDOM.flushSync(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In here, I've decided to wrap the whole onChange
handler to "batch" all updates scheduled by within this callback
I guess I forgot to update the lock file - just fixed that |
Can you run 🙏 |
@benoitgrelard I've just done this - please verify if this is how you want to bump packages based on this change |
It's all good apart from the main workspace which should be declined instead of patch 👍 |
Do you think we should do those too then? |
Closing in favour of #1378, thanks again for your help on this 🙏 |
fixes #1287
Ok, so this is what I got from a quick debugging session:
contextmenuchange
isn't dispatched at all in this case and thus you can't prevent it and fire a custom logic for itpointer-events: none
on the body, hereresetPointerEvents
is not called "soon enough" to unblock the mentionedcontextmenuchange
eventI've attached
onOpenChange
to theContextMenu.Root
and just logged the received value together with some other stuff.This is the order of the things happening under the hood:
React 17:
resetPointerEvents()
->onOpenChange(false)
React 18:
onOpenChange(false)
->resetPointerEvents()
As we can see... this somewhat matches the info that I've gathered when debugging. The order is flipped and this leads to a special sort of race condition.
Based on my intuition I've just deduced that this might behave slightly differently in terms of "flushing" the updates as there were some changes to that in React 18. I was looking for a rogue
setState
, to wrap it withflushSync
but then I've realized that during debugging I've seen someCustomEvent
stuff and since React is usingwindow.event
to determine the update priority... it made sense to just wrap all of those custom events withflushSync
. You might want to be more granular, but I think that it's safer to just merge this and then reexamine if some of your custom events should actually not be wrapped with this.