Description
Merges multiple refs into a single ref.
This is useful when building UI components that have a local ref,
but also support external refs via React.forwardRef
.
Code Byte
import React from 'react';
export function mergeRefs<T>(
refs: Array<React.Ref<T> | undefined>
): React.RefCallback<T> | null {
if (refs.every((ref) => ref == null)) {
return null;
}
return (value) => {
refs.forEach((ref) => {
if (typeof ref === 'function') {
ref(value);
} else if (ref != null) {
(ref as React.MutableRefObject<T | null>).current = value;
}
});
};
}
/**
* @description Merges multiple refs into a single ref.
* This is useful when building UI components that have a local ref,
* but also support external refs via `React.forwardRef`.
*
* @example
* const Component = React.forwardRef((props, ref) => {
* const localRef = React.useRef();
* const mergedRef = useMergeRefs([localRef, ref]);
*
* return <div ref={mergedRef} {...props} />;
* });
* */
export function useMergeRefs<T>(
refs: Array<React.Ref<T> | undefined>
): React.RefCallback<T> | null {
return React.useMemo(() => {
return mergeRefs(refs);
}, refs);
}
Arguments
Arguments | Type | Default | Required | Description |
---|---|---|---|---|
dispatch | Array<React.Ref<T> | undefined> | - | ✅ | A list of refs to merge. |
Usage
const Component = React.forwardRef((props, ref) => {
const localRef = React.useRef();
const mergedRef = useMergeRefs([localRef, ref]);
return <div ref={mergedRef} {...props} />;
});
Tests
import { renderHook } from '@testing-library/react-hooks';
import { useMergeRefs } from './useMergeRefs.hook';
describe('useMergeRefs()', () => {
it('should merge multiple refs into a single ref', () => {
const ref1 = { current: null };
const ref2 = { current: null };
const ref3 = { current: null };
const { result } = renderHook(() =>
useMergeRefs<string>([ref1, ref2, ref3])
);
const mergedRef = result.current;
mergedRef?.('test');
expect(ref1.current).toBe('test');
expect(ref2.current).toBe('test');
expect(ref3.current).toBe('test');
});
it('should return null if all refs are null', () => {
const { result } = renderHook(() => useMergeRefs([null, null, null]));
expect(result.current).toBeNull();
});
it('should return null if all refs are undefined', () => {
const { result } = renderHook(() =>
useMergeRefs([undefined, undefined, undefined])
);
expect(result.current).toBeNull();
});
});