import type {RefObject} from 'react'; import {useMemo} from 'react'; import {useSyncedRef} from '../useSyncedRef/index.js'; export type HookableRefHandler = (v: T) => T; export function useHookableRef( initialValue: T, onSet?: HookableRefHandler, onGet?: HookableRefHandler, ): RefObject; export function useHookableRef(): RefObject; /** * Like `React.useRef` but it is possible to define get and set handlers. * * @param initialValue Initial value of a hook. * @param onSet Function to be called while ref.current value set. Return value * will be stored in ref. * @param onGet Function to be called while ref.current value accessed. Return * value will be used as a return value. */ export function useHookableRef( initialValue?: T, onSet?: HookableRefHandler, onGet?: HookableRefHandler, ): RefObject { const onSetRef = useSyncedRef(onSet); const onGetRef = useSyncedRef(onGet); return useMemo(() => { let v = initialValue; return { get current() { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return onGetRef.current === undefined ? v : onGetRef.current(v as T); }, set current(value) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion v = onSetRef.current === undefined ? value : onSetRef.current(value as T); }, }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); }