-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Update some React 18 related types #45279
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
Conversation
|
Size Change: +206 B (0%) Total Size: 1.28 MB
ℹ️ View Unchanged
|
ciampo
left a comment
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
helpto 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
ciampo
left a comment
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-typesandnpm build:packagesreported errors like:and

The only one I'm not fixing is the
AnimatePresencechildren, which requires upgrade offramer-motionpackage to the latest, React 18 enabled version, with updated types.IsolatedEventContainer: theforwardRefcallback argument used to inferchildrenautomatically for thepropsparameter, but with React 18 one has to specifychildrenexplicitly, by default thepropstype is{}. I rewrote this component to TypeScript, and specified the props fordivelement as the component's props -- it passes through everything to the innerdiv.FormFileUpload: there is mismatch a between thevoidreturn type of therenderfunction andundefinedvalue that's valid forchildren. Surprisingly,voidcan't be assigned toundefinedand type inference for Reactchildrengets confused. I reshuffled the offending code so that the return value ofrender()is not assigned to anything.ToggleControl: thehelpprop can be aReactNodeor a render prop callback that has a( checked: boolean ) => ReactNodetype. But the callback type was never declared. I think it used to work before React 18 only because theReactNodetype includedFunctionand now it no longer does.Popover: adding type annotations toarrowRefso that the types are not inferred asanyornull.useFocusOutside: there is a JSDoc type annotation for theeventparameter 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 thenodeparameter. Adding the type reveals a type bug: we are adding a DOM listener, but using the ReactKeyboardEventtype for it, but it's not a React listener! We need to use the DOMKeyboardEventtype. React events are synthetic, they are wrappers around the native DOM events.