useMergeRef.js 1.6 KB

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