MCPcopy
hub / github.com/vercel/next.js / useMergedRef

Function useMergedRef

packages/next/src/client/use-merged-ref.ts:10–48  ·  view source on GitHub ↗
(
  refA: Ref<TElement>,
  refB: Ref<TElement>
)

Source from the content-addressed store, hash-verified

8// We know refs are always called alternating with `null` and then `T`.
9// So a call with `null` means we need to call the previous cleanup functions.
10export function useMergedRef<TElement>(
11 refA: Ref<TElement>,
12 refB: Ref<TElement>
13): Ref<TElement> {
14 const cleanupA = useRef<(() => void) | null>(null)
15 const cleanupB = useRef<(() => void) | null>(null)
16
17 // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
18 // (this happens often if the user doesn't pass a ref to Link/Form/Image)
19 // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
20 // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
21 // (because it hasn't been updated for React 19)
22 // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
23 // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
24 return useCallback(
25 (current: TElement | null): void => {
26 if (current === null) {
27 const cleanupFnA = cleanupA.current
28 if (cleanupFnA) {
29 cleanupA.current = null
30 cleanupFnA()
31 }
32 const cleanupFnB = cleanupB.current
33 if (cleanupFnB) {
34 cleanupB.current = null
35 cleanupFnB()
36 }
37 } else {
38 if (refA) {
39 cleanupA.current = applyRef(refA, current)
40 }
41 if (refB) {
42 cleanupB.current = applyRef(refB, current)
43 }
44 }
45 },
46 [refA, refB]
47 )
48}
49
50function applyRef<TElement>(
51 refA: NonNullable<Ref<TElement>>,

Callers 4

link.tsxFile · 0.90
LinkComponentFunction · 0.90
FormFunction · 0.90

Calls 1

applyRefFunction · 0.85

Tested by

no test coverage detected