Last active
October 3, 2023 08:16
-
-
Save simonrelet/8da2f8463f8e36c7dadc6aeef974e6f4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useMemo } from "react"; | |
export function useComposedRef<TElement>(handlers: RefHandler<TElement>[]) { | |
// We actually want the handlers to be the dependency list. | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
return useMemo(() => composeRefs(handlers), handlers); | |
} | |
function composeRefs<TElement>( | |
handlers: RefHandler<TElement>[], | |
): React.MutableRefObject<null | TElement> { | |
let element: null | TElement = null; | |
return { | |
get current() { | |
return element; | |
}, | |
set current(value) { | |
setRefs(value, handlers); | |
element = value; | |
}, | |
}; | |
} | |
function setRefs<TElement>( | |
element: null | TElement, | |
handlers: RefHandler<TElement>[], | |
) { | |
handlers.forEach((handler) => { | |
// Ingore falsy handlers to allow syntaxes like: | |
// | |
// ```js | |
// const child = React.Children.only(this.props.children) | |
// return React.cloneElement(child, { | |
// ref: ref => setRefs(ref, [this.myRef, child.ref]) | |
// }) | |
// ``` | |
if (handler) { | |
switch (typeof handler) { | |
case "function": { | |
handler(element); | |
break; | |
} | |
case "object": { | |
handler.current = element; | |
break; | |
} | |
default: | |
throw new Error( | |
`Only refs of type function and React.createRef() are supported. Got ${typeof handler}.`, | |
); | |
} | |
} | |
}); | |
} | |
type RefHandler<TElement> = | |
| React.RefCallback<null | TElement> | |
| React.MutableRefObject<null | TElement> | |
| undefined | |
| null; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment