import { useEffect, useMemo, useRef } from "react"; type UseIntervalTypes = { callback: () => void; delay: number; }; /** * React lifecycle-aware implementation of setInterval. * This allows for a quick implementation of setInterval in React without having to worry about cleanup. * @see https://overreacted.io/making-setinterval-declarative-with-react-hooks/ by Dan Abramov for more details. */ function useInterval( callback: UseIntervalTypes["callback"], delay: UseIntervalTypes["delay"] ) { const runOnMount = useRef(true); const savedCallback = useRef(); const intervalRef = useRef(); useEffect(() => { return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, []); // Remember the latest callback. useEffect(() => { savedCallback.current = callback; }, [callback]); // Run the interval if this hook mounts and runOnMount is true // eslint-disable-next-line consistent-return useEffect(() => { function tick() { savedCallback.current?.(); } if (delay !== null && runOnMount.current) { intervalRef.current = setInterval(tick, delay); } }, [delay]); // Run the interval when this function is called const beginPolling = () => { function tick() { savedCallback.current?.(); } intervalRef.current = setInterval(tick, delay); }; // Intercept how this function is called const interceptedReturn = useMemo(() => { return new Proxy([beginPolling], { get(target, prop, receiver) { // If beginPolling is extracted on the call (ie. const [poll] = useInterval(_, 5000)), // do not start the interval on mount. if (!Number.isNaN(prop) && prop === "0") { runOnMount.current = false; } return Reflect.get(target, prop, receiver); }, }); }, [beginPolling]); return interceptedReturn; } export default useInterval;