-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Update some React 18 related types #45279
Conversation
Size Change: +206 B (0%) Total Size: 1.28 MB
ℹ️ View Unchanged
|
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.
ToggleControl: the help prop can be a ReactNode or a render prop callback that has a ( checked: boolean ) => ReactNode type. But the callback type was never declared. I think it used to work before React 18 only because the ReactNode type included Function and now it no longer does.
Interesting. I had a look at definitely typed (v18, v17) but I couldn't spot the difference 🤷
useDialog: specify the type of the node parameter. Adding the type reveals a type bug: we are adding a DOM listener, but using the React KeyboardEvent type for it, but it's not a React listener! We need to use the DOM KeyboardEvent type. React events are synthetic, they are wrappers around the native DOM events.
Thank you for spotting that! Sometimes these types are automatically added by the IDE — not sure if that's the case here, but definitely a subtle bug to spot.
I tracked it down now and found that |
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.
[...] With the v18 types, the intersection with Iterable is empty and the result is never.
Thank you for the sleuthing, super interesting!
Before merging, could you add a CHANGELOG entry too?
// `checked` is passed down from parent component. Uncontrolled | ||
// component can show only a static help label. | ||
if ( checked !== undefined ) { | ||
helpLabel = help( checked ); |
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 wonder if it would make sense to allow help
to be called with an undefined
, as it currently is on trunk
. The docs don't mention anything about help
working only for controlled components
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.
could you add a CHANGELOG entry too?
Added an entry about the ToggleControl
's help
prop type. Other changes in this PR are not publicly visible.
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 wonder if it would make sense to allow
help
to be called with anundefined
That's an incorrect behavior though. If the checked
prop is not specified, then the input
in uncontrolled, i.e., it stores the checked
state internally and only fires the onChange
events. The user can check the toggle, it will fire the onChange
event, will be checked visually, but the checked
prop variable will still be undefined
. The wrong help
will be shown.
After a closer look, I see that ToggleControl
doesn't really support uncontrolled mode correctly. A checked FormToggle
component won't have the is-checked
CSS class, because the checked
prop is never true
. Also the component doesn't have a defaultChecked
prop for setting initial uncontrolled value.
The checked
value shouldn't be allowed to be undefined
. It only creates an illusion of supporting uncontrolled mode, but the support is not really there. And all usages in the Gutenberg codebase seem to be controlled.
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.
Thank you again for looking into this! Your reasoning makes sense to me.
After a closer look, I see that ToggleControl doesn't really support uncontrolled mode correctly.
That seems to be an issue most of the older control components in the library, thank you for flagging
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.
LGTM 🚀
React 18 also has updated type definitions that are sometimes more strict and require fixes in our code. This PR does some type updates that came up when working on #45235.
npm build:package-types
andnpm build:packages
reported errors like:and
The only one I'm not fixing is the
AnimatePresence
children, which requires upgrade offramer-motion
package to the latest, React 18 enabled version, with updated types.IsolatedEventContainer
: theforwardRef
callback argument used to inferchildren
automatically for theprops
parameter, but with React 18 one has to specifychildren
explicitly, by default theprops
type is{}
. I rewrote this component to TypeScript, and specified the props fordiv
element as the component's props -- it passes through everything to the innerdiv
.FormFileUpload
: there is mismatch a between thevoid
return type of therender
function andundefined
value that's valid forchildren
. Surprisingly,void
can't be assigned toundefined
and type inference for Reactchildren
gets confused. I reshuffled the offending code so that the return value ofrender()
is not assigned to anything.ToggleControl
: thehelp
prop can be aReactNode
or a render prop callback that has a( checked: boolean ) => ReactNode
type. But the callback type was never declared. I think it used to work before React 18 only because theReactNode
type includedFunction
and now it no longer does.Popover
: adding type annotations toarrowRef
so that the types are not inferred asany
ornull
.useFocusOutside
: there is a JSDoc type annotation for theevent
parameter but it annotates the result ofuseCallback
, not the inner function. I moved the annotations inside so that the types are not inferred asany
.useDialog
: specify the type of thenode
parameter. Adding the type reveals a type bug: we are adding a DOM listener, but using the ReactKeyboardEvent
type for it, but it's not a React listener! We need to use the DOMKeyboardEvent
type. React events are synthetic, they are wrappers around the native DOM events.