-
-
Notifications
You must be signed in to change notification settings - Fork 32.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
[core] Improve getReactElementRef() utils #43022
Conversation
Netlify deploy previewhttps://deploy-preview-43022--material-ui.netlify.app/ Bundle size reportDetails of bundle changes (Toolpad) |
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.
Left some comments, but let's wait for @oliviertassinari since he suggested the improvements.
return null; | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (!React.isValidElement(child)) { | ||
console.error( |
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'm missing something. Do we have a concrete example where this warning helps and is not harming (creating confusion)?
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.
You are right, console.error
doesn't provide any help if child
is invalid react element. Throwing error instead of logging error seems to improve DX.
Code used to get below errors
<Slide direction="up" in={checked} mountOnEnter unmountOnExit>
<p>sample</p>
<p>sample</p>
</Slide>
when just console.error
is used
When thowing error instead of console.error
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.
@sai6855 I think this example
<Slide direction="up" in={checked} mountOnEnter unmountOnExit>
<p>sample</p>
<p>sample</p>
</Slide>
supports the previous argument I was trying to develop in #42818 (comment): we increase the bundle size for end-users, so we degrade the UX. But on the opposite side, the error is still not actionable, so we don't improve the DX.
I conclude that it makes things worse.
Previously, the experience was (without TypeScript):
which feels equal.
So this didff looks like a step forward to me. Simpler, less bundle size, faster runtime:
diff --git a/packages/mui-material/src/utils/getChildRef.js b/packages/mui-material/src/utils/getChildRef.js
index a1d0c2aec5..c24cd926d3 100644
--- a/packages/mui-material/src/utils/getChildRef.js
+++ b/packages/mui-material/src/utils/getChildRef.js
@@ -1,9 +1,4 @@
-import * as React from 'react';
-
export default function getChildRef(child) {
- if (!child || !React.isValidElement(child)) {
- return null;
- }
// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in React 18
// below check is to ensure 'ref' is accessible in both cases
return child.props.propertyIsEnumerable('ref') ? child.props.ref : child.ref;
Now, if we want to avoid the confusion for developers, to not have an error thrown, but only a console error message, I could see us doing. This feels like a better DX:
diff --git a/packages/mui-material/src/Slide/Slide.js b/packages/mui-material/src/Slide/Slide.js
index d669e811d6..5b3a7a71ed 100644
--- a/packages/mui-material/src/Slide/Slide.js
+++ b/packages/mui-material/src/Slide/Slide.js
@@ -119,6 +119,10 @@ const Slide = React.forwardRef(function Slide(props, ref) {
...other
} = props;
+ // Invalid children provided, bailout
+ if (!React.isValidElement(children)) {
+ return children;
+ }
+
const childrenRef = React.useRef(null);
const handleRef = useForkRef(getChildRef(children), childrenRef, ref);
diff --git a/packages/mui-material/src/utils/getChildRef.js b/packages/mui-material/src/utils/getChildRef.js
index a1d0c2aec5..c24cd926d3 100644
--- a/packages/mui-material/src/utils/getChildRef.js
+++ b/packages/mui-material/src/utils/getChildRef.js
@@ -1,9 +1,4 @@
-import * as React from 'react';
-
export default function getChildRef(child) {
- if (!child || !React.isValidElement(child)) {
- return null;
- }
// 'ref' is passed as prop in React 19, whereas 'ref' is directly attached to children in React 18
// below check is to ensure 'ref' is accessible in both cases
return child.props.propertyIsEnumerable('ref') ? child.props.ref : child.ref;
Now, there will also be the React 19 prop-types feedback problem for people now using types or using any
:
There, I think that we simply need to create new utils, inspired by facebook/react#28328, and do:
diff --git a/packages/mui-material/src/Slide/Slide.js b/packages/mui-material/src/Slide/Slide.js
index d669e811d6..f316556579 100644
--- a/packages/mui-material/src/Slide/Slide.js
+++ b/packages/mui-material/src/Slide/Slide.js
@@ -1,6 +1,8 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
+import getDisplayName from '@mui/utils/getDisplayName';
+import checkPropTypes from 'prop-types/checkPropTypes';
import { Transition } from 'react-transition-group';
import chainPropTypes from '@mui/utils/chainPropTypes';
import HTMLElementType from '@mui/utils/HTMLElementType';
@@ -82,11 +84,20 @@ export function setTranslateValue(direction, node, containerProp) {
}
}
+function muiCheckPropTypes(Component, props,) {
+ if (process.env.NODE_ENV === 'production') {
+ return;
+ }
+ return checkPropTypes(Component.propTypes, props, 'prop', getDisplayName(Component));
+}
+
/**
* The Slide transition is used by the [Drawer](/material-ui/react-drawer/) component.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
-const Slide = React.forwardRef(function Slide(props, ref) {
+const Slide = React.forwardRef(function SlideIn(props, ref) {
+ muiCheckPropTypes(Slide, props);
+
cc @mui/code-infra
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 feels like a better DX:
I second that.
There, I think that we simply need to create new utils
Sure, I perhaps would consider moving the condition around muiCheckPropTypes(Slide, props);
to shave off a few more bytes and prevent an unnecessary empty function call.
+const Slide = React.forwardRef(function SlideIn(props, ref) {
+ if (process.env.NODE_ENV !== 'production') {
+ muiCheckPropTypes(Slide, props);
+ }
+
Do we have a CI check that verifies certain utilities don't make it into production bundles?
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 perhaps would consider moving the condition around muiCheckPropTypes(Slide, props); to shave off a few more bytes and prevent an unnecessary empty function call.
Fair enough.
It's also missing a React.version check to not warn twice in React 18 and lower.
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.
Opportunity moved to #43138
4bfa892
to
916aca6
Compare
@sai6855 The changes make sense to me now. We are back to a local maximum 👍. If we want to get to the next local maximum, I would see: diff --git a/packages/mui-material/src/Slide/Slide.js b/packages/mui-material/src/Slide/Slide.js
index d669e811d6..5b3a7a71ed 100644
--- a/packages/mui-material/src/Slide/Slide.js
+++ b/packages/mui-material/src/Slide/Slide.js
@@ -119,6 +119,10 @@ const Slide = React.forwardRef(function Slide(props, ref) {
...other
} = props;
+ // Invalid children provided, bailout
+ if (!React.isValidElement(children)) {
+ return children;
+ }
+
const childrenRef = React.useRef(null);
const handleRef = useForkRef(getChildRef(children), childrenRef, ref); combined with #43138, but better to be iterative. |
@sai6855 as part of the React 19 effort, @DiegoAndai recently moved |
df6c02d
to
348814b
Compare
d819c4d
to
7dcfa23
Compare
Summary of changes done in this PR
|
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.
Looks good.
I don't have a strong opinion on using propertyIsEnumerable
vs check the React major. Although I tend to prefer to check features over versions, the version check works well, too. If someone has a strong opinion, speak now 😄
143e570
to
b84c85d
Compare
Thank you @sai6855! |
check #42818 (comment) for more context