From c5f47ce81a847a1274ab2f7753b1903f4eb916d2 Mon Sep 17 00:00:00 2001 From: LiuLiu Date: Wed, 8 Apr 2026 09:38:33 -0700 Subject: [PATCH] =?UTF-8?q?Revert=20"Replace=20`useRefObjectAsForwardedRef?= =?UTF-8?q?`=20with=20`useMergedRefs`=20internally=20=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f04e85df536cd6e8047f70b070bd3cf7c0961f92. --- .changeset/combined-refs-hook.md | 5 ----- packages/react/src/ActionBar/ActionBar.tsx | 6 +++--- packages/react/src/ActionList/Heading.tsx | 6 +++--- packages/react/src/Autocomplete/Autocomplete.test.tsx | 1 - packages/react/src/Autocomplete/AutocompleteInput.tsx | 6 +++--- .../react/src/Autocomplete/AutocompleteOverlay.tsx | 6 +++--- packages/react/src/Button/ButtonBase.tsx | 7 ++++--- packages/react/src/Dialog/Dialog.tsx | 6 +++--- packages/react/src/Heading/Heading.tsx | 6 +++--- packages/react/src/Link/Link.tsx | 7 ++++--- packages/react/src/Overlay/Overlay.tsx | 6 +++--- packages/react/src/PageLayout/PageLayout.tsx | 10 +++++----- .../src/TextInputWithTokens/TextInputWithTokens.tsx | 6 +++--- packages/react/src/deprecated/DialogV1/Dialog.tsx | 6 +++--- .../react/src/hooks/__tests__/useMergedRefs.test.tsx | 4 ++-- packages/react/src/hooks/useMergedRefs.ts | 8 ++++---- 16 files changed, 46 insertions(+), 50 deletions(-) delete mode 100644 .changeset/combined-refs-hook.md diff --git a/.changeset/combined-refs-hook.md b/.changeset/combined-refs-hook.md deleted file mode 100644 index e8e9c96e0cc..00000000000 --- a/.changeset/combined-refs-hook.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@primer/react': patch ---- - -Update internal implementations of combined refs to improve performance and add support for React 19 callback refs diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 2ba3b086b9c..83712ce21fd 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -12,7 +12,7 @@ import {ActionMenu} from '../ActionMenu' import {useFocusZone, FocusKeys} from '../hooks/useFocusZone' import styles from './ActionBar.module.css' import {clsx} from 'clsx' -import {useMergedRefs} from '../hooks' +import {useRefObjectAsForwardedRef} from '../hooks' import {createDescendantRegistry} from '../utils/descendant-registry' const ACTIONBAR_ITEM_GAP = 8 @@ -470,7 +470,7 @@ function useWidth(ref: React.RefObject) { export const ActionBarIconButton = forwardRef( ({disabled, onClick, ...props}: ActionBarIconButtonProps, forwardedRef) => { const ref = useRef(null) - const mergedRef = useMergedRefs(ref, forwardedRef) + useRefObjectAsForwardedRef(forwardedRef, ref) const {size, isVisibleChild} = React.useContext(ActionBarContext) const {groupId} = React.useContext(ActionBarGroupContext) @@ -507,7 +507,7 @@ export const ActionBarIconButton = forwardRef( return ( { const innerRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) const {headingId: headingId, variant: listVariant} = React.useContext(ListContext) const {container} = React.useContext(ActionListContainerContext) @@ -37,7 +37,7 @@ export const Heading = forwardRef(({as, size, children, visuallyHidden = false, { const inputNode = getByLabelText(AUTOCOMPLETE_LABEL) expect(inputNode.getAttribute('aria-expanded')).not.toBe('true') - inputNode.focus() fireEvent.click(inputNode) fireEvent.keyDown(inputNode, {key: 'ArrowDown'}) diff --git a/packages/react/src/Autocomplete/AutocompleteInput.tsx b/packages/react/src/Autocomplete/AutocompleteInput.tsx index ef1278451a1..61b2905e5ba 100644 --- a/packages/react/src/Autocomplete/AutocompleteInput.tsx +++ b/packages/react/src/Autocomplete/AutocompleteInput.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useContext, useEffect, useState} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import {AutocompleteContext, AutocompleteInputContext} from './AutocompleteContext' import TextInput from '../TextInput' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {ComponentProps} from '../utils/types' import useSafeTimeout from '../hooks/useSafeTimeout' @@ -43,7 +43,7 @@ const AutocompleteInput = React.forwardRef( } const {activeDescendantRef, id, inputRef, setInputValue, setShowMenu, showMenu} = autocompleteContext const {autocompleteSuggestion = '', inputValue = '', isMenuDirectlyActivated} = inputContext - const mergedRef = useMergedRefs(forwardedRef, inputRef) + useRefObjectAsForwardedRef(forwardedRef, inputRef) const [highlightRemainingText, setHighlightRemainingText] = useState(true) const {safeSetTimeout} = useSafeTimeout() @@ -160,7 +160,7 @@ const AutocompleteInput = React.forwardRef( onKeyDown={handleInputKeyDown} onKeyPress={onInputKeyPress} onKeyUp={handleInputKeyUp} - ref={mergedRef} + ref={inputRef} aria-controls={`${id}-listbox`} aria-autocomplete="both" role="combobox" diff --git a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx index 9d810b90f75..0755434225e 100644 --- a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx +++ b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx @@ -5,7 +5,7 @@ import type {OverlayProps} from '../Overlay' import Overlay from '../Overlay' import type {ComponentProps} from '../utils/types' import {AutocompleteContext} from './AutocompleteContext' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import VisuallyHidden from '../_VisuallyHidden' import classes from './AutocompleteOverlay.module.css' @@ -57,7 +57,7 @@ function AutocompleteOverlay({ [showMenu, selectedItemLength], ) - const mergedRef = useMergedRefs(scrollContainerRef, floatingElementRef) + useRefObjectAsForwardedRef(scrollContainerRef, floatingElementRef) const closeOptionList = useCallback(() => { setShowMenu(false) @@ -73,7 +73,7 @@ function AutocompleteOverlay({ preventFocusOnOpen={true} onClickOutside={closeOptionList} onEscape={closeOptionList} - ref={mergedRef} + ref={floatingElementRef as React.RefObject} top={position?.top} left={position?.left} className={clsx(classes.Overlay, className)} diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index 8642e7a04f4..9600a6f86b8 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -1,7 +1,7 @@ import React, {forwardRef, type JSX} from 'react' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import type {ButtonProps} from './types' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import {VisuallyHidden} from '../VisuallyHidden' import Spinner from '../Spinner' import CounterLabel from '../CounterLabel' @@ -51,7 +51,7 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f } = props const innerRef = React.useRef(null) - const combinedRefs = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) const uuid = useId(id) const loadingAnnouncementID = `${uuid}-loading-announcement` @@ -87,7 +87,8 @@ const ButtonBase = forwardRef(({children, as: Component = 'button', ...props}, f (null) - const mergedRef = useMergedRefs(forwardedRef, dialogRef) + useRefObjectAsForwardedRef(forwardedRef, dialogRef) const backdropRef = useRef(null) useFocusTrap({ @@ -388,7 +388,7 @@ const _Dialog = React.forwardRef
{ const innerRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, innerRef) + useRefObjectAsForwardedRef(forwardedRef, innerRef) if (__DEV__) { /** @@ -32,7 +32,7 @@ const Heading = forwardRef(({as: Component = 'h2', className, variant, ...props} }, [innerRef]) } - return + return }) as PolymorphicForwardRefComponent Heading.displayName = 'Heading' diff --git a/packages/react/src/Link/Link.tsx b/packages/react/src/Link/Link.tsx index 574f9a38cef..65d08ac1424 100644 --- a/packages/react/src/Link/Link.tsx +++ b/packages/react/src/Link/Link.tsx @@ -1,6 +1,6 @@ import {clsx} from 'clsx' import React, {useEffect, type ForwardedRef, type ElementRef} from 'react' -import {useMergedRefs} from '../hooks' +import {useRefObjectAsForwardedRef} from '../hooks' import classes from './Link.module.css' import type {ComponentProps} from '../utils/types' import {type PolymorphicProps, fixedForwardRef} from '../utils/modern-polymorphic' @@ -20,7 +20,7 @@ export const UnwrappedLink = ( ) => { const {as: Component = 'a', className, inline, muted, hoverColor, ...restProps} = props const innerRef = React.useRef>(null) - const mergedRef = useMergedRefs(ref, innerRef) + useRefObjectAsForwardedRef(ref, innerRef) if (__DEV__) { /** @@ -53,7 +53,8 @@ export const UnwrappedLink = ( data-inline={inline} data-hover-color={hoverColor} {...restProps} - ref={mergedRef} + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ref={innerRef as any} /> ) } diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx index f495d015730..0ba9d556419 100644 --- a/packages/react/src/Overlay/Overlay.tsx +++ b/packages/react/src/Overlay/Overlay.tsx @@ -5,7 +5,7 @@ import type {AriaRole, Merge} from '../utils/types' import type {TouchOrMouseEvent} from '../hooks' import {useOverlay} from '../hooks' import Portal from '../Portal' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {AnchorSide} from '@primer/behaviors' import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic' import classes from './Overlay.module.css' @@ -194,7 +194,7 @@ const Overlay = React.forwardRef( ): ReactElement => { const featureFlagMaxHeightClampToViewport = useFeatureFlag('primer_react_overlay_max_height_clamp_to_viewport') const overlayRef = useRef(null) - const mergedRef = useMergedRefs(forwardedRef, overlayRef) + useRefObjectAsForwardedRef(forwardedRef, overlayRef) const slideAnimationDistance = 8 // var(--base-size-8), hardcoded to do some math const slideAnimationEasing = 'cubic-bezier(0.33, 1, 0.68, 1)' const cssAnchorPositioning = useFeatureFlag('primer_react_css_anchor_positioning') @@ -239,7 +239,7 @@ const Overlay = React.forwardRef( role={role} width={width} data-reflow-container={!preventOverflow ? true : undefined} - ref={mergedRef} + ref={overlayRef} left={leftPosition} right={right} height={height} diff --git a/packages/react/src/PageLayout/PageLayout.tsx b/packages/react/src/PageLayout/PageLayout.tsx index 0cee74e68a4..97946a17f55 100644 --- a/packages/react/src/PageLayout/PageLayout.tsx +++ b/packages/react/src/PageLayout/PageLayout.tsx @@ -1,7 +1,7 @@ import React, {memo, useRef} from 'react' import {clsx} from 'clsx' import {useId} from '../hooks/useId' -import {useMergedRefs} from '../hooks/useMergedRefs' +import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import type {ResponsiveValue} from '../hooks/useResponsiveValue' import {isResponsiveValue} from '../hooks/useResponsiveValue' import {useSlots} from '../hooks/useSlots' @@ -838,7 +838,7 @@ const Pane = React.forwardRef
)}
(null) const selectedValuesDescriptionId = useId() - const mergedRef = useMergedRefs(forwardedRef, ref) + useRefObjectAsForwardedRef(forwardedRef, ref) const [selectedTokenIndex, setSelectedTokenIndex] = useState() const [tokensAreTruncated, setTokensAreTruncated] = useState(Boolean(visibleTokenCount)) const selectedTokenTexts = tokens @@ -310,7 +310,7 @@ function TextInputWithTokensInnerComponent
( ) => { const overlayRef = useRef(null) const modalRef = useRef(null) - const mergedRef = useMergedRefs(forwardedRef, modalRef) + useRefObjectAsForwardedRef(forwardedRef, modalRef) const closeButtonRef = useRef(null) const onCloseClick = () => { @@ -73,7 +73,7 @@ const Dialog = forwardRef( const Component = forwardRef(({asButton}, forwardedRef) => { const ref: InputOrButtonRef = React.useRef(null) - const mergedRef = useMergedRefs(forwardedRef, ref) + const combinedRef = useMergedRefs(forwardedRef, ref) - return asButton ?