@guanghechen/react-hooks
Version:
106 lines (95 loc) • 3.23 kB
JavaScript
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 };