useMergeRef.js 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. import * as React from 'react';
  2. import { assignRef } from './assignRef';
  3. import { useCallbackRef } from './useRef';
  4. const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
  5. const currentValues = new WeakMap();
  6. /**
  7. * Merges two or more refs together providing a single interface to set their value
  8. * @param {RefObject|Ref} refs
  9. * @returns {MutableRefObject} - a new ref, which translates all changes to {refs}
  10. *
  11. * @see {@link mergeRefs} a version without buit-in memoization
  12. * @see https://github.com/theKashey/use-callback-ref#usemergerefs
  13. * @example
  14. * const Component = React.forwardRef((props, ref) => {
  15. * const ownRef = useRef();
  16. * const domRef = useMergeRefs([ref, ownRef]); // 👈 merge together
  17. * return <div ref={domRef}>...</div>
  18. * }
  19. */
  20. export function useMergeRefs(refs, defaultValue) {
  21. const callbackRef = useCallbackRef(defaultValue || null, (newValue) => refs.forEach((ref) => assignRef(ref, newValue)));
  22. // handle refs changes - added or removed
  23. useIsomorphicLayoutEffect(() => {
  24. const oldValue = currentValues.get(callbackRef);
  25. if (oldValue) {
  26. const prevRefs = new Set(oldValue);
  27. const nextRefs = new Set(refs);
  28. const current = callbackRef.current;
  29. prevRefs.forEach((ref) => {
  30. if (!nextRefs.has(ref)) {
  31. assignRef(ref, null);
  32. }
  33. });
  34. nextRefs.forEach((ref) => {
  35. if (!prevRefs.has(ref)) {
  36. assignRef(ref, current);
  37. }
  38. });
  39. }
  40. currentValues.set(callbackRef, refs);
  41. }, [refs]);
  42. return callbackRef;
  43. }