diff --git a/packages/mui-material/src/Fade/Fade.js b/packages/mui-material/src/Fade/Fade.js index 0258a3adae8723..3996c5f885ae75 100644 --- a/packages/mui-material/src/Fade/Fade.js +++ b/packages/mui-material/src/Fade/Fade.js @@ -6,6 +6,7 @@ import elementAcceptingRef from '@mui/utils/elementAcceptingRef'; import useTheme from '../styles/useTheme'; import { reflow, getTransitionProps } from '../transitions/utils'; import useForkRef from '../utils/useForkRef'; +import getChildRef from '../utils/getChildRef'; const styles = { entering: { @@ -48,7 +49,7 @@ const Fade = React.forwardRef(function Fade(props, ref) { const enableStrictModeCompat = true; const nodeRef = React.useRef(null); - const handleRef = useForkRef(nodeRef, children.ref, ref); + const handleRef = useForkRef(nodeRef, getChildRef(children), ref); const normalizedTransitionCallback = (callback) => (maybeIsAppearing) => { if (callback) { diff --git a/packages/mui-material/src/Grow/Grow.js b/packages/mui-material/src/Grow/Grow.js index 77866f1f107765..45e433d8601f33 100644 --- a/packages/mui-material/src/Grow/Grow.js +++ b/packages/mui-material/src/Grow/Grow.js @@ -7,6 +7,7 @@ import { Transition } from 'react-transition-group'; import useTheme from '../styles/useTheme'; import { getTransitionProps, reflow } from '../transitions/utils'; import useForkRef from '../utils/useForkRef'; +import getChildRef from '../utils/getChildRef'; function getScale(value) { return `scale(${value}, ${value ** 2})`; @@ -61,7 +62,7 @@ const Grow = React.forwardRef(function Grow(props, ref) { const theme = useTheme(); const nodeRef = React.useRef(null); - const handleRef = useForkRef(nodeRef, children.ref, ref); + const handleRef = useForkRef(nodeRef, getChildRef(children), ref); const normalizedTransitionCallback = (callback) => (maybeIsAppearing) => { if (callback) { diff --git a/packages/mui-material/src/Slide/Slide.js b/packages/mui-material/src/Slide/Slide.js index f2d30a7a97d588..12a3b8ac3fbdc2 100644 --- a/packages/mui-material/src/Slide/Slide.js +++ b/packages/mui-material/src/Slide/Slide.js @@ -10,6 +10,7 @@ import useForkRef from '../utils/useForkRef'; import useTheme from '../styles/useTheme'; import { reflow, getTransitionProps } from '../transitions/utils'; import { ownerWindow } from '../utils'; +import getChildRef from '../utils/getChildRef'; // Translate the node so it can't be seen on the screen. // Later, we're going to translate the node back to its original location with `none`. @@ -119,7 +120,7 @@ const Slide = React.forwardRef(function Slide(props, ref) { } = props; const childrenRef = React.useRef(null); - const handleRef = useForkRef(children.ref, childrenRef, ref); + const handleRef = useForkRef(getChildRef(children), childrenRef, ref); const normalizedTransitionCallback = (callback) => (isAppearing) => { if (callback) { diff --git a/packages/mui-material/src/Tooltip/Tooltip.js b/packages/mui-material/src/Tooltip/Tooltip.js index a0dd1879ffa26c..115c252aee7c88 100644 --- a/packages/mui-material/src/Tooltip/Tooltip.js +++ b/packages/mui-material/src/Tooltip/Tooltip.js @@ -19,6 +19,7 @@ import useId from '../utils/useId'; import useIsFocusVisible from '../utils/useIsFocusVisible'; import useControlled from '../utils/useControlled'; import tooltipClasses, { getTooltipUtilityClass } from './tooltipClasses'; +import getChildRef from '../utils/getChildRef'; function round(value) { return Math.round(value * 1e5) / 1e5; @@ -485,7 +486,7 @@ const Tooltip = React.forwardRef(function Tooltip(inProps, ref) { }; }, [handleClose, open]); - const handleRef = useForkRef(children.ref, focusVisibleRef, setChildNode, ref); + const handleRef = useForkRef(getChildRef(children), focusVisibleRef, setChildNode, ref); // There is no point in displaying an empty tooltip. // So we exclude all falsy values, except 0, which is valid. diff --git a/packages/mui-material/src/Zoom/Zoom.js b/packages/mui-material/src/Zoom/Zoom.js index 5f0ebfd5d9f781..945357ac828cc2 100644 --- a/packages/mui-material/src/Zoom/Zoom.js +++ b/packages/mui-material/src/Zoom/Zoom.js @@ -6,6 +6,7 @@ import elementAcceptingRef from '@mui/utils/elementAcceptingRef'; import useTheme from '../styles/useTheme'; import { reflow, getTransitionProps } from '../transitions/utils'; import useForkRef from '../utils/useForkRef'; +import getChildRef from '../utils/getChildRef'; const styles = { entering: { @@ -48,7 +49,7 @@ const Zoom = React.forwardRef(function Zoom(props, ref) { } = props; const nodeRef = React.useRef(null); - const handleRef = useForkRef(nodeRef, children.ref, ref); + const handleRef = useForkRef(nodeRef, getChildRef(children), ref); const normalizedTransitionCallback = (callback) => (maybeIsAppearing) => { if (callback) { diff --git a/packages/mui-material/src/utils/getChildRef.d.ts b/packages/mui-material/src/utils/getChildRef.d.ts new file mode 100644 index 00000000000000..c7e9c058d3e031 --- /dev/null +++ b/packages/mui-material/src/utils/getChildRef.d.ts @@ -0,0 +1 @@ +export default function getChildRef(child: React.ReactElement): React.Ref | null; diff --git a/packages/mui-material/src/utils/getChildRef.js b/packages/mui-material/src/utils/getChildRef.js new file mode 100644 index 00000000000000..a1d0c2aec5af0c --- /dev/null +++ b/packages/mui-material/src/utils/getChildRef.js @@ -0,0 +1,10 @@ +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; +}