Skip to content

useMergedRefs

Edit

Combines callback and object refs into one React ref callback.

useMergedRefs.ts
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);
}
}, []);
}
Terminal
wget -O src/hooks/useMergedRefs.ts https://raw.githubusercontent.com/jrTilak/lazykit/HEAD/registry/react-hooks/useMergedRefs.ts
useMergedRefs.example.tsx
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.

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.