useMergedRefs
EditCombines callback and object refs into one React ref callback.
import { useCallback, useEffect, useLayoutEffect, useRef,} from "react";
import type { Ref, RefCallback } from "react";
export type MergeableRef<Value> = Ref<Value> | null | undefined;
const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
function assignRef<Value>(ref: Ref<Value>, value: Value | null): void { if (typeof ref === "function") { ref(value); return; }
(ref as { current: Value | null }).current = value;}
export function useMergedRefs<Value>( ...refs: readonly MergeableRef<Value>[]): RefCallback<Value> { const refsRef = useRef(refs); const valueRef = useRef<Value | null>(null); const hasAssignedRef = useRef(false);
useIsomorphicLayoutEffect(() => { const previousRefs = refsRef.current; refsRef.current = refs;
if (!hasAssignedRef.current) return;
for (const ref of previousRefs) { if ( ref !== null && ref !== undefined && !refs.includes(ref) ) { assignRef(ref, null); } }
for (const ref of refs) { if ( ref !== null && ref !== undefined && !previousRefs.includes(ref) ) { assignRef(ref, valueRef.current); } } });
return useCallback((value: Value | null) => { valueRef.current = value; hasAssignedRef.current = true; for (const ref of refsRef.current) { if (ref !== null && ref !== undefined) assignRef(ref, value); } }, []);}Download
Section titled “Download”wget -O src/hooks/useMergedRefs.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/react-hooks/useMergedRefs.tstemp_dir="$(mktemp -d)" && bunx degit jrTilak/lazykit/registry/react-hooks "$temp_dir" && mv "$temp_dir/useMergedRefs.ts" src/hooks/useMergedRefs.ts && rm -r "$temp_dir"Examples
Section titled “Examples”import { useRef, useState } from "react";
import { useMergedRefs } from "./useMergedRefs";
export function MergedRefsExample() { const inputRef = useRef<HTMLInputElement>(null); const [mountedNode, setMountedNode] = useState<HTMLInputElement | null>(null); const mergedRef = useMergedRefs(inputRef, setMountedNode);
return ( <div> <input ref={mergedRef} defaultValue="Both refs receive this input" /> <button type="button" onClick={() => inputRef.current?.focus()}> Focus input </button> <p>{mountedNode === null ? "Input is detached" : "Input is attached"}</p> </div> );}refs(readonly (Ref<Value> | null | undefined)[]) — Callback or object refs that should all receive the same value.
Returns
Section titled “Returns”RefCallback<Value> — A merged callback that assigns the mounted value and
null cleanup to every supplied ref. Its identity stays stable when refs are
added, removed, or reordered; newly added refs receive the current value and
removed refs receive null after commit.