Skip to content

Commit 522f580

Browse files
authored
Replace useCombinedRefs with useRefObjectAsForwardedRef (#2204)
* Replace `useCombinedRefs` with `useRefObjectAsForwardedRef` * Create red-wombats-whisper.md
1 parent b78170c commit 522f580

File tree

11 files changed

+49
-71
lines changed

11 files changed

+49
-71
lines changed

.changeset/red-wombats-whisper.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
Replace `useCombinedRefs` with `useRefObjectAsForwardedRef`

src/Autocomplete/AutocompleteInput.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React, {
22
ChangeEventHandler,
33
FocusEventHandler,
44
KeyboardEventHandler,
5-
MutableRefObject,
65
useCallback,
76
useContext,
87
useEffect,
@@ -11,7 +10,7 @@ import React, {
1110
import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
1211
import {AutocompleteContext} from './AutocompleteContext'
1312
import TextInput from '../TextInput'
14-
import {useCombinedRefs} from '../hooks/useCombinedRefs'
13+
import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef'
1514
import {ComponentProps} from '../utils/types'
1615

1716
type InternalAutocompleteInputProps = {
@@ -39,7 +38,7 @@ const AutocompleteInput = React.forwardRef(
3938
setShowMenu,
4039
showMenu
4140
} = autocompleteContext
42-
const combinedInputRef = useCombinedRefs(inputRef, forwardedRef)
41+
useRefObjectAsForwardedRef(forwardedRef, inputRef)
4342
const [highlightRemainingText, setHighlightRemainingText] = useState<boolean>(true)
4443

4544
const handleInputFocus: FocusEventHandler<HTMLInputElement> = useCallback(
@@ -58,12 +57,12 @@ const AutocompleteInput = React.forwardRef(
5857
// this prevents the menu from hiding when the user is clicking an option in the Autoselect.Menu,
5958
// but still hides the menu when the user blurs the input by tabbing out or clicking somewhere else on the page
6059
setTimeout(() => {
61-
if (document.activeElement !== combinedInputRef.current) {
60+
if (document.activeElement !== inputRef.current) {
6261
setShowMenu(false)
6362
}
6463
}, 0)
6564
},
66-
[onBlur, setShowMenu, combinedInputRef]
65+
[onBlur, setShowMenu, inputRef]
6766
)
6867

6968
const handleInputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
@@ -157,7 +156,7 @@ const AutocompleteInput = React.forwardRef(
157156
onKeyDown={handleInputKeyDown}
158157
onKeyPress={onInputKeyPress}
159158
onKeyUp={handleInputKeyUp}
160-
ref={combinedInputRef as MutableRefObject<HTMLInputElement>}
159+
ref={inputRef}
161160
aria-controls={`${id}-listbox`}
162161
aria-autocomplete="both"
163162
role="combobox"

src/Autocomplete/AutocompleteOverlay.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {useAnchoredPosition} from '../hooks'
33
import Overlay, {OverlayProps} from '../Overlay'
44
import {ComponentProps} from '../utils/types'
55
import {AutocompleteContext} from './AutocompleteContext'
6-
import {useCombinedRefs} from '../hooks/useCombinedRefs'
6+
import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef'
77

88
type AutocompleteOverlayInternalProps = {
99
/**
@@ -39,7 +39,7 @@ function AutocompleteOverlay({
3939
[showMenu, selectedItemLength]
4040
)
4141

42-
const combinedOverlayRef = useCombinedRefs(scrollContainerRef, floatingElementRef)
42+
useRefObjectAsForwardedRef(scrollContainerRef, floatingElementRef)
4343

4444
const closeOptionList = useCallback(() => {
4545
setShowMenu(false)
@@ -55,7 +55,7 @@ function AutocompleteOverlay({
5555
preventFocusOnOpen={true}
5656
onClickOutside={closeOptionList}
5757
onEscape={closeOptionList}
58-
ref={combinedOverlayRef as React.RefObject<HTMLDivElement>}
58+
ref={floatingElementRef as React.RefObject<HTMLDivElement>}
5959
top={position?.top}
6060
left={position?.left}
6161
visibility={showMenu ? 'visible' : 'hidden'}

src/Dialog.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import useDialog from './hooks/useDialog'
77
import sx, {SxProp} from './sx'
88
import Text from './Text'
99
import {ComponentProps} from './utils/types'
10-
import {useCombinedRefs} from './hooks/useCombinedRefs'
10+
import {useRefObjectAsForwardedRef} from './hooks/useRefObjectAsForwardedRef'
1111

1212
const noop = () => null
1313

@@ -95,7 +95,8 @@ type InternalDialogProps = {
9595
const Dialog = forwardRef<HTMLDivElement, InternalDialogProps>(
9696
({children, onDismiss = noop, isOpen, initialFocusRef, returnFocusRef, ...props}, forwardedRef) => {
9797
const overlayRef = useRef(null)
98-
const modalRef = useCombinedRefs(forwardedRef)
98+
const modalRef = useRef<HTMLDivElement>(null)
99+
useRefObjectAsForwardedRef(forwardedRef, modalRef)
99100
const closeButtonRef = useRef(null)
100101

101102
const onCloseClick = () => {

src/Dialog/Dialog.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {XIcon} from '@primer/octicons-react'
1111
import {useFocusZone} from '../hooks/useFocusZone'
1212
import {FocusKeys} from '@primer/behaviors'
1313
import Portal from '../Portal'
14-
import {useCombinedRefs} from '../hooks/useCombinedRefs'
14+
import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef'
1515
import {useSSRSafeId} from '@react-aria/ssr'
1616

1717
const ANIMATION_DURATION = '200ms'
@@ -274,7 +274,7 @@ const _Dialog = React.forwardRef<HTMLDivElement, React.PropsWithChildren<DialogP
274274
const defaultedProps = {...props, title, subtitle, role, dialogLabelId, dialogDescriptionId}
275275

276276
const dialogRef = useRef<HTMLDivElement>(null)
277-
const combinedRef = useCombinedRefs(dialogRef, forwardedRef)
277+
useRefObjectAsForwardedRef(forwardedRef, dialogRef)
278278
const backdropRef = useRef<HTMLDivElement>(null)
279279
useFocusTrap({containerRef: dialogRef, restoreFocusOnCleanUp: true, initialFocusRef: autoFocusedFooterButtonRef})
280280

@@ -297,7 +297,7 @@ const _Dialog = React.forwardRef<HTMLDivElement, React.PropsWithChildren<DialogP
297297
<StyledDialog
298298
width={width}
299299
height={height}
300-
ref={combinedRef}
300+
ref={dialogRef}
301301
role={role}
302302
aria-labelledby={dialogLabelId}
303303
aria-describedby={dialogDescriptionId}

src/Overlay.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {AriaRole, Merge} from './utils/types'
66
import {useOverlay, TouchOrMouseEvent} from './hooks'
77
import Portal from './Portal'
88
import sx, {SxProp} from './sx'
9-
import {useCombinedRefs} from './hooks/useCombinedRefs'
9+
import {useRefObjectAsForwardedRef} from './hooks/useRefObjectAsForwardedRef'
1010
import type {AnchorSide} from '@primer/behaviors'
1111
import {useTheme} from './ThemeProvider'
1212
import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
@@ -142,7 +142,7 @@ const Overlay = React.forwardRef<HTMLDivElement, OwnOverlayProps>(
142142
forwardedRef
143143
): ReactElement => {
144144
const overlayRef = useRef<HTMLDivElement>(null)
145-
const combinedRef = useCombinedRefs(overlayRef, forwardedRef)
145+
useRefObjectAsForwardedRef(forwardedRef, overlayRef)
146146
const {theme} = useTheme()
147147
const slideAnimationDistance = parseInt(get('space.2')(theme).replace('px', ''))
148148
const slideAnimationEasing = get('animation.easeOutCubic')(theme)
@@ -158,10 +158,10 @@ const Overlay = React.forwardRef<HTMLDivElement, OwnOverlayProps>(
158158
})
159159

160160
useEffect(() => {
161-
if (height === 'initial' && combinedRef.current?.clientHeight) {
162-
combinedRef.current.style.height = `${combinedRef.current.clientHeight}px`
161+
if (height === 'initial' && overlayRef.current?.clientHeight) {
162+
overlayRef.current.style.height = `${overlayRef.current.clientHeight}px`
163163
}
164-
}, [height, combinedRef])
164+
}, [height])
165165

166166
useLayoutEffect(() => {
167167
const {x, y} = getSlideAnimationStartingVector(anchorSide)
@@ -185,7 +185,7 @@ const Overlay = React.forwardRef<HTMLDivElement, OwnOverlayProps>(
185185
height={height}
186186
role={role}
187187
{...rest}
188-
ref={combinedRef}
188+
ref={overlayRef}
189189
style={
190190
{
191191
top: `${top || 0}px`,

src/TextInputWithTokens.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import {isFocusable} from '@primer/behaviors/utils'
33
import {omit} from '@styled-system/props'
44
import React, {FocusEventHandler, KeyboardEventHandler, MouseEventHandler, RefObject, useRef, useState} from 'react'
55
import Box from './Box'
6-
import {useProvidedRefOrCreate} from './hooks'
7-
import {useCombinedRefs} from './hooks/useCombinedRefs'
6+
import {useRefObjectAsForwardedRef} from './hooks/useRefObjectAsForwardedRef'
87
import {useFocusZone} from './hooks/useFocusZone'
98
import Text from './Text'
109
import {TextInputProps} from './TextInput'
@@ -93,12 +92,11 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
9392
visibleTokenCount,
9493
...rest
9594
}: TextInputWithTokensProps<TokenComponentType>,
96-
externalRef: React.ForwardedRef<HTMLInputElement>
95+
forwardedRef: React.ForwardedRef<HTMLInputElement>
9796
) {
9897
const {onBlur, onFocus, onKeyDown, ...inputPropsRest} = omit(rest)
99-
const ref = useProvidedRefOrCreate<HTMLInputElement>(externalRef as React.RefObject<HTMLInputElement>)
100-
const localInputRef = useRef<HTMLInputElement>(null)
101-
const combinedInputRef = useCombinedRefs(localInputRef, ref)
98+
const ref = useRef<HTMLInputElement>(null)
99+
useRefObjectAsForwardedRef(forwardedRef, ref)
102100
const [selectedTokenIndex, setSelectedTokenIndex] = useState<number | undefined>()
103101
const [tokensAreTruncated, setTokensAreTruncated] = useState<boolean>(Boolean(visibleTokenCount))
104102
const {containerRef} = useFocusZone(
@@ -124,7 +122,7 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
124122
}
125123

126124
if (nextIndex > tokens.length || nextIndex < 1) {
127-
return combinedInputRef.current || undefined
125+
return ref.current || undefined
128126
}
129127

130128
return containerRef.current?.children[nextIndex] as HTMLElement
@@ -230,7 +228,7 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
230228
}
231229

232230
const focusInput: MouseEventHandler = () => {
233-
combinedInputRef.current?.focus()
231+
ref.current?.focus()
234232
}
235233

236234
const preventTokenClickPropagation: MouseEventHandler = event => {
@@ -323,7 +321,7 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
323321
}}
324322
>
325323
<UnstyledTextInput
326-
ref={combinedInputRef}
324+
ref={ref}
327325
disabled={disabled}
328326
onFocus={handleInputFocus}
329327
onBlur={handleInputBlur}

src/drafts/InlineAutocomplete/InlineAutocomplete.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, {cloneElement, useRef} from 'react'
22
import Box from '../../Box'
3-
import {useCombinedRefs} from '../../hooks/useCombinedRefs'
43
import {useSyntheticChange} from '../hooks/useSyntheticChange'
54
import Portal from '../../Portal'
65
import {BetterSystemStyleObject} from '../../sx'
@@ -14,6 +13,7 @@ import {
1413
requireChildrenToBeInput
1514
} from './utils'
1615
import AutocompleteSuggestions from './_AutocompleteSuggestions'
16+
import {useRefObjectAsForwardedRef} from '../../hooks'
1717

1818
export type InlineAutocompleteProps = {
1919
/** Register the triggers that can cause suggestions to appear. */
@@ -86,7 +86,9 @@ const InlineAutocomplete = ({
8686
// Forward accessibility props so it works with FormControl
8787
...forwardProps
8888
}: InlineAutocompleteProps & React.ComponentProps<'textarea' | 'input'>) => {
89-
const inputRef = useCombinedRefs(children.ref)
89+
const inputRef = useRef<HTMLInputElement & HTMLTextAreaElement>(null)
90+
useRefObjectAsForwardedRef(children.ref ?? noop, inputRef)
91+
9092
const externalInput = requireChildrenToBeInput(children, inputRef)
9193

9294
const emitSyntheticChange = useSyntheticChange({

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export {useProvidedStateOrCreate} from './useProvidedStateOrCreate'
1313
export {useMenuInitialFocus} from './useMenuInitialFocus'
1414
export {useMenuKeyboardNavigation} from './useMenuKeyboardNavigation'
1515
export {useMnemonics} from './useMnemonics'
16+
export {useRefObjectAsForwardedRef} from './useRefObjectAsForwardedRef'

src/hooks/useCombinedRefs.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)