-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(react-tooltip): In StrictMode, Tooltip now shows correctly on elements that are focused when created #34331
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
Open
behowell
wants to merge
4
commits into
microsoft:master
Choose a base branch
from
behowell:tooltip-delay-visible-strict-mode
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
fix(react-tooltip): In StrictMode, Tooltip now shows correctly on elements that are focused when created #34331
behowell
wants to merge
4
commits into
microsoft:master
from
behowell:tooltip-delay-visible-strict-mode
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
📊 Bundle size reportUnchanged fixtures
|
Pull request demo site: URL |
@@ -0,0 +1,7 @@ | |||
{ |
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.
🕵🏾♀️ visual changes to review in the Visual Change Report
vr-tests-react-components/Avatar Converged 1 screenshots
Image Name | Diff(in Pixels) | Image Type |
---|---|---|
vr-tests-react-components/Avatar Converged.badgeMask - RTL.normal.chromium.png | 3 | Changed |
vr-tests-react-components/Positioning 2 screenshots
Image Name | Diff(in Pixels) | Image Type |
---|---|---|
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png | 182 | Changed |
vr-tests-react-components/Positioning.Positioning end.chromium.png | 837 | Changed |
vr-tests-react-components/TagPicker 2 screenshots
Image Name | Diff(in Pixels) | Image Type |
---|---|---|
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png | 659 | Changed |
vr-tests-react-components/TagPicker.disabled.disabled input hover.chromium.png | 678 | Changed |
There were 2 duplicate changes discarded. Check the build logs for more information.
marcosmoura
approved these changes
May 9, 2025
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.
This seems like a good option for now.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Previous Behavior
Tooltip uses a custom utilitiy
useTimeout
(useBrowserTimer
) to manage the timer that delays showing the Tooltip. This utility creates the timer through an imperative function, but clears the timer in a useEffect cleanup function. This results in the tooltip never being shown if it is unmounted and remounted after the Tooltip was triggered but before it was shown. This can happen in StrictMode for a component that receives initial focus when it is created, such as a MenuItem.New Behavior
Add a ref that saves the arguments to the pending visibility change, whenever a delayed visibility change is triggered. Then, add a useEffect that runs when the component is mounted, and checks the ref to see if there is a pending change that was never completed. If so, restart the timer.
Discussion
There are a number of different options for a fix here, with varying levels of impact on runtime performance.
The fix in this PR has the least impact on runtime performance and behavior. However, it adds an additional suboptimal
useEffect
call to restart the timer on mount and "fix" theuseEffect
inuseBrowserTimer
that cancels the timer on unmount.The "correct" fix would be to move the timer to be started and cleared entirely within a
useEffect
. Then use a state variable to track that the tooltip was triggered, which would cause the useEffect to be run upon re-rendering. Example code is below. This has a major drawback of re-rendering the tooltipped component any time the Tooltip timer is started or stopped, which happens on every pointerEnter, pointerLeave, focus, or blur event, in addition to re-rendering whenever the tooltip shows or hides (and in React <=17, it actually adds two extra renders per show or hide, because state changes are not coalesced when using timers).The net effect is that showing a tooltip goes from 1 re-render to 2 or 3 re-renders (same for hiding the tooltip), and a quick mouseover/mouseout goes from 0 re-renders to 2-re-renders.
There are a few other possible fixes that involve moving the timer to be within a separate child component of the Tooltip. E.g. we could move all of the visibility state management into a new "TooltipContent" component that would replace the
div
currently used for thecontent
slot. The major benefit to that would be that the tooltipped component would never have any re-renders caused by the tooltip (it currently re-renders once every time the tooltip shows or hides). Unfortunately that would most likely be a breaking change to the API of Tooltip, so it's not really an option for a bug fix.Related Issue(s)