-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
ToggleGroupControl
: react correctly to external controlled updates
#56678
ToggleGroupControl
: react correctly to external controlled updates
#56678
Conversation
ToggleGroupControl
: react better to external controlled updatesToggleGroupControl
: react correctly to external controlled updates
Flaky tests detected in 749a884. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7052472587
|
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.
Yes, this fixes the bug. The problem was that the render where valueProp
changed from foo
to bar
was still returning value: undefined
, because the hasEver
value is still false
. It's updated only a bit later in the effect.
This PR immediately schedules a next render where hasEver
will be true
and it will return value: 'bar'
.
An implementation that doesn't need the extra render but returns the right value immediately when valueProp
changes:
function useComputedControlled( value ) {
const prevValue = usePrevious( value );
const prevControlled = useRef( false );
const isControlled = prevControlled.current || ( prevValue.current !== undefined && value !== undefined && prevValue.current !== value );
useEffect( () => {
prevControlled.current = isControlled;
}, [ isControlled ] );
if ( isControlled ) {
return { value: value ?? '', defaultValue: undefined };
}
return { value: undefined, defaultValue: value };
}
valueProp !== previousValueProp; | ||
previousValueProp !== undefined && | ||
valueProp !== previousValueProp | ||
); |
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.
I'd change this to:
if ( ! hasEver && definedAndDifferent( prevVal, val ) {
setHasEver( true );
}
That prevents scheduling setState
calls that will be noops anyway, changing false
to false
.
Also, once in controlled mode, the component can't change back to uncontrolled, right? The component always starts as uncontrolled, and switches to controlled on the first re-render that has a different valueProp
.
Then a better name for hasEver...
is simply inControlledMode
.
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.
That prevents scheduling setState calls that will be noops anyway, changing false to false.
That's a good suggestion, thank you!
Also, once in controlled mode, the component can't change back to uncontrolled, right?
Yes, this custom behaviour was necessary to be backwards compatible with how the component used to work before supporting uncontrolled mode.
I'm not going to implement this suggestion since I decided to refactor the hook following your other suggestion
I like this! I went ahead and refactored the hook following your suggestion, plus with some minor adjustments (for example, I'll wait for folks to have a final round of review before merging |
2924e4d
to
749a884
Compare
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 fixes the issue and doesn't seem to break other instances of ToggleGroupControl
. Changes LGTM and thanks for adding the test!
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.
Thanks for accepting my suggestions 😉 Now I approve the PR even more.
Fixes #56667
Fixes the issue described in #56500
What?
This PR improves the internal logic in
ToggleGroupControl
used to detect whether the component is used in controlled or uncontrolled moreWhy?
The previous logic wasn't reacting correctly to new values being passed to the component, and as a result it wasn't causing the component to update with the new values.
How?
Using
useState
instead ofuseRef
to track internal state causes the component to re-render more eagerly when the "controlled" flag flips.Testing Instructions
ToggleGroupControlAsRadioGroup
value is always undefined #56667ToggleGroupControl
's unit tests — the newly added test should fail without the fix in place.