UNPKG

@guanghechen/react-hooks

Version:
106 lines (95 loc) 3.23 kB
import { useRef, useCallback, useLayoutEffect, useEffect, useMemo, useState } from 'react'; import { isEqual } from '@guanghechen/equal'; function useDeepCompareCallback(fn, deps) { const signal = useRef(0); const prevDeps = useRef(deps); if (!isEqual(prevDeps.current, deps)) signal.current += 1; prevDeps.current = deps; return useCallback(fn, [signal.current]); } function useEventCallback(handler) { const handlerRef = useRef(handler); useLayoutEffect(() => { handlerRef.current = handler; }); return useCallback((...args) => { const handle = handlerRef.current; return handle(...args); }, []); } function useRefreshRef(value) { const ref = useRef(value); ref.current = value; return ref; } function useBeforeUnloadEffect(fn, deps) { const beforeunload = useDeepCompareCallback(fn, deps); useLayoutEffect(() => { window.addEventListener('beforeunload', beforeunload); return () => window.removeEventListener('beforeunload', beforeunload); }, [beforeunload]); } function useBeforeUnloadAsyncEffect(fn, preventDefault = true) { const preventDefaultRef = useRefreshRef(preventDefault); const beforeunload = useEventCallback(async (event) => { if (preventDefaultRef.current === false) { return fn(event); } event.preventDefault(); try { await fn(event); } finally { delete event.returnValue; } }); useLayoutEffect(() => { window.addEventListener('beforeunload', beforeunload); return () => window.removeEventListener('beforeunload', beforeunload); }, [beforeunload]); } function useDeepCompareEffect(fn, deps) { const signal = useRef(0); const prevDeps = useRef(deps); if (!isEqual(prevDeps.current, deps)) signal.current += 1; prevDeps.current = deps; useEffect(fn, [signal.current]); } function useDeepCompareMemo(fn, deps) { const signal = useRef(0); const prevDeps = useRef(deps); if (!isEqual(prevDeps.current, deps)) signal.current += 1; prevDeps.current = deps; return useMemo(fn, [signal.current]); } function useInterval(callback, duration) { const callbackRef = useRef(callback); useEffect(() => { callbackRef.current = callback; }, [callback]); useEffect(() => { const tick = () => { if (callbackRef.current === undefined) return; callbackRef.current(); }; const intervalId = setInterval(tick, duration); return () => clearInterval(intervalId); }, [duration]); } function usePreviousState(value) { const ref = useRef(value); useEffect(() => { ref.current = value; }, [value]); return ref.current; } function useSyncState(initialState) { const [state, setState] = useState(initialState); useEffect(() => setState(initialState), [initialState]); return [state, setState]; } export { useBeforeUnloadAsyncEffect, useBeforeUnloadEffect, useDeepCompareCallback, useDeepCompareEffect, useDeepCompareMemo, useEventCallback, useInterval, usePreviousState, useRefreshRef, useSyncState };