Skip to content

Commit 09858e0

Browse files
authored
fix: remove element.ref warning (#1580)
1 parent 9c5fb78 commit 09858e0

File tree

4 files changed

+33
-40
lines changed

4 files changed

+33
-40
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sanity/ui",
3-
"version": "2.11.6",
3+
"version": "2.11.7-canary.0",
44
"keywords": [
55
"sanity",
66
"ui",

src/core/primitives/popover/popover.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {origin} from '../../middleware/origin'
2828
import {useTheme_v2} from '../../theme'
2929
import {BoxOverflow, CardTone, Placement, PopoverMargins} from '../../types'
3030
import {LayerProps, LayerProvider, Portal, useBoundaryElement} from '../../utils'
31+
import {getElementRef} from '../../utils/getElementRef'
3132
import {ResponsiveRadiusProps, ResponsiveShadowProps} from '../types'
3233
import {
3334
DEFAULT_POPOVER_DISTANCE,
@@ -443,29 +444,3 @@ export const Popover = memo(
443444
}),
444445
)
445446
Popover.displayName = 'Memo(ForwardRef(Popover))'
446-
447-
// Before React 19 accessing `element.props.ref` will throw a warning and suggest using `element.ref`
448-
// After React 19 accessing `element.ref` does the opposite.
449-
// https://github.com/facebook/react/pull/28348
450-
//
451-
// Access the ref using the method that doesn't yield a warning.
452-
function getElementRef(element: React.JSX.Element) {
453-
// React <=18 in DEV
454-
let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get
455-
let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning
456-
457-
if (mayWarn) {
458-
return (element as any).ref
459-
}
460-
461-
// React 19 in DEV
462-
getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get
463-
mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning
464-
465-
if (mayWarn) {
466-
return element.props.ref
467-
}
468-
469-
// Not DEV
470-
return element.props.ref || (element as any).ref
471-
}

src/core/primitives/tooltip/tooltip.tsx

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {origin} from '../../middleware/origin'
3030
import {useTheme_v2} from '../../theme'
3131
import type {Placement} from '../../types'
3232
import {Layer, type LayerProps, Portal, useBoundaryElement, usePortal} from '../../utils'
33+
import {getElementRef} from '../../utils/getElementRef'
3334
import type {Delay} from '../types'
3435
import {
3536
DEFAULT_FALLBACK_PLACEMENTS,
@@ -320,11 +321,6 @@ export const Tooltip = forwardRef(function Tooltip(
320321
[refs],
321322
)
322323

323-
const childRef = useRef<HTMLElement | null>(null)
324-
325-
// Merge refs so that any ref we are overriding is called as well
326-
useImperativeHandle((childProp as any)?.ref, () => childRef.current)
327-
328324
const child = useMemo(() => {
329325
if (!childProp) return null
330326

@@ -335,7 +331,7 @@ export const Tooltip = forwardRef(function Tooltip(
335331
onMouseLeave: handleMouseLeave,
336332
onClick: handleClick,
337333
onContextMenu: handleContextMenu,
338-
ref: childRef,
334+
ref: setReferenceElement,
339335
})
340336
}, [
341337
childProp,
@@ -349,13 +345,9 @@ export const Tooltip = forwardRef(function Tooltip(
349345

350346
// If there's a child then we need to set the reference element to the cloned child ref
351347
// and if child changes we make sure to update or remove the reference element.
352-
useEffect(() => {
353-
if (!child) return undefined
354-
355-
setReferenceElement(childRef.current)
356-
357-
return () => setReferenceElement(null)
358-
}, [child])
348+
useImperativeHandle(childProp ? getElementRef(childProp) : null, () => referenceElement, [
349+
referenceElement,
350+
])
359351

360352
if (!child) return <></>
361353

src/core/utils/getElementRef.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Based on https://github.com/radix-ui/primitives/blob/0bade6a704e5821b90a6da0f3d8cfa8a7711127d/packages/react/slot/src/Slot.tsx#L128-L150
2+
// Before React 19 accessing `element.props.ref` will throw a warning and suggest using `element.ref`
3+
// After React 19 accessing `element.ref` does the opposite.
4+
// https://github.com/facebook/react/pull/28348
5+
//
6+
// Access the ref using the method that doesn't yield a warning.
7+
export function getElementRef(element: React.ReactElement) {
8+
// React <=18 in DEV
9+
let getter = Object.getOwnPropertyDescriptor(element.props, 'ref')?.get
10+
let mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning
11+
12+
if (mayWarn) {
13+
return (element as any).ref
14+
}
15+
16+
// React 19 in DEV
17+
getter = Object.getOwnPropertyDescriptor(element, 'ref')?.get
18+
mayWarn = getter && 'isReactWarning' in getter && getter.isReactWarning
19+
20+
if (mayWarn) {
21+
return (element.props as {ref?: React.Ref<unknown>}).ref
22+
}
23+
24+
// Not DEV
25+
return (element.props as {ref?: React.Ref<unknown>}).ref || (element as any).ref
26+
}

0 commit comments

Comments
 (0)