@primer/react
Version:
An implementation of GitHub's Primer Design System using React
87 lines (81 loc) • 2.53 kB
JavaScript
import { c } from 'react-compiler-runtime';
import 'react';
/**
* Combine two refs of matching type (typically an external or forwarded ref and an internal `useRef` object or
* callback ref).
*
* If you need to combine more than two refs (what are you doing?) just nest the hook:
* `useMergedRefs(refA, useMergedRefs(refB, refC))`
*
* @param refA First ref to merge. The order is not important.
* @param refB Second ref to merge. The order is not important.
* @returns A new ref which must be passed to the relevant child component. **Important**: do not pass `refA` or
* `refB` to the component!
*
* @example
* // React 18
* const Example = forwardRef<HTMLButtonElement, {}>((props, forwardedRef) => {
* const ref = useRef<HTMLButtonElement>(null)
* const combinedRef = useMergedRefs(forwardedRef, ref)
*
* return <button ref={combinedRef} />
* })
*
* @example
* // React 19
* const Example = ({ref: externalRef}: {ref?: Ref<HTMLButtonElement>}) => {
* const ref = useRef<HTMLButtonElement>(null)
* const combinedRef = useMergedRefs(externalRef, ref)
*
* return <button ref={combinedRef} />
* }
*/
function useMergedRefs(refA, refB) {
const $ = c(3);
let t0;
if ($[0] !== refA || $[1] !== refB) {
t0 = value => {
const cleanupA = setRef(refA, value);
const cleanupB = setRef(refB, value);
return () => {
if (cleanupA) {
cleanupA();
} else {
setRef(refA, null);
}
if (cleanupB) {
cleanupB();
} else {
setRef(refB, null);
}
};
};
$[0] = refA;
$[1] = refB;
$[2] = t0;
} else {
t0 = $[2];
}
return t0;
}
/**
* React 19 supports callback refs that can return a cleanup function. If a cleanup function is returned, the
* cleanup is called on unmount **instead** of setting the ref to null.
*/
// bivarianceHack copied from React types
/**
* Supporting React 18 and 19 while alleviating the need for any hacks or casts in consumers:
* - `ForwardedRef` from the React 18 `forwardRef` HOC
* - `React19RefCallback` for callback refs that can return a cleanup function (this is included in `Ref` in React 19
* but not in 18)
* - `Ref` for standard refs from `useRef` or passed in as React 19 prop
* - `undefined` to allow for easy use of optional `ref` props in React 19
*/
function setRef(ref, value) {
if (typeof ref === 'function') {
return ref(value);
} else if (ref) {
ref.current = value;
}
}
export { useMergedRefs };