feat: add useCombinedRefs hook

This commit is contained in:
Sergey Krylov 2024-05-13 07:34:19 +03:00
parent 126050912c
commit e427da99e3
2 changed files with 56 additions and 0 deletions

View File

@ -0,0 +1 @@
export { default as useCombinedRefs } from './useCombineRef';

View File

@ -0,0 +1,55 @@
/**
* A combined ref implementation using the callback ref cleanups feature.
* Note that this won't work yet as callback ref cleanups feature isn't released yet and thought to be released in React 19
*/
import { useCallback } from 'react';
import type { ForwardedRef } from 'react';
type OptionalRef<T> = ForwardedRef<T> | undefined;
type Cleanup = (() => void) | undefined;
const setRef = <T,>(ref: OptionalRef<T>, value: T): Cleanup => {
if (typeof ref === 'function') {
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
const cleanup = ref(value);
if (typeof cleanup === 'function') {
return cleanup;
}
// eslint-disable-next-line @stylistic/max-statements-per-line
return () => { ref(null); };
}
if (ref) {
// eslint-disable-next-line no-param-reassign
ref.current = value;
// eslint-disable-next-line no-param-reassign,no-return-assign
return () => ref.current = null;
}
// eslint-disable-next-line no-undefined
return undefined;
};
const useCombinedRefs = <T,>(...refs: OptionalRef<T>[]) => useCallback((value: T | null) => {
const cleanups: Cleanup[] = [];
for (const ref of refs) {
const cleanup = setRef(ref, value);
cleanups.push(cleanup);
}
return () => {
for (const cleanup of cleanups) {
cleanup?.();
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, refs);
export default useCombinedRefs;