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();
  });
});