Skip to content

Commit

Permalink
Ensure merging refs does not trigger warnings in React 18
Browse files Browse the repository at this point in the history
test plan:
- link-ref-app
- link-ref-pages
  • Loading branch information
eps1lon committed Sep 2, 2024
1 parent 2332c10 commit a20455e
Showing 1 changed file with 23 additions and 18 deletions.
41 changes: 23 additions & 18 deletions packages/next/src/client/use-merged-ref.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
import { useMemo, type Ref } from 'react'
import { useMemo, useRef, type Ref } from 'react'

// This is a compatibility hook to support React 18 and 19 refs.
// In 19, a cleanup function from refs may be returned.
// In 18, returning a cleanup function creates a warning.
// Since we take userspace refs, we don't know ahead of time if a cleanup function will be returned.
// This implements cleanup functions with the old behavior in 18.
// We know refs are always called alternating with `null` and then `T`.
// So a call with `null` means we need to call the previous cleanup functions.
export function useMergedRef<TElement>(
refA: Ref<TElement>,
refB: Ref<TElement>
): Ref<TElement> {
return useMemo(() => mergeRefs(refA, refB), [refA, refB])
}
const cleanupA = useRef<() => void>(() => {})
const cleanupB = useRef<() => void>(() => {})

export function mergeRefs<TElement>(
refA: Ref<TElement>,
refB: Ref<TElement>
): Ref<TElement> {
if (!refA || !refB) {
return refA || refB
}

return (current: TElement) => {
const cleanupA = applyRef(refA, current)
const cleanupB = applyRef(refB, current)
return useMemo(() => {
if (!refA || !refB) {
return refA || refB
}

return () => {
cleanupA()
cleanupB()
return (current: TElement | null): void => {
if (current === null) {
cleanupA.current()
cleanupB.current()
} else {
cleanupA.current = applyRef(refA, current)
cleanupB.current = applyRef(refB, current)
}
}
}
}, [refA, refB])
}

function applyRef<TElement>(
Expand Down

0 comments on commit a20455e

Please sign in to comment.