import { useEffect } from 'react'; import useCommittedRef from './useCommittedRef'; /** * Creates a `setInterval` that is properly cleaned up when a component unmounted * * ```tsx * function Timer() { * const [timer, setTimer] = useState(0) * useInterval(() => setTimer(i => i + 1), 1000) * * return {timer} seconds past * } * ``` * * @param fn an function run on each interval * @param ms The milliseconds duration of the interval */ function useInterval(fn, ms, paused, runImmediately) { if (paused === void 0) { paused = false; } if (runImmediately === void 0) { runImmediately = false; } var handle; var fnRef = useCommittedRef(fn); // this ref is necessary b/c useEffect will sometimes miss a paused toggle // orphaning a setTimeout chain in the aether, so relying on it's refresh logic is not reliable. var pausedRef = useCommittedRef(paused); var tick = function tick() { if (pausedRef.current) return; fnRef.current(); schedule(); // eslint-disable-line no-use-before-define }; var schedule = function schedule() { clearTimeout(handle); handle = setTimeout(tick, ms); }; useEffect(function () { if (runImmediately) { tick(); } else { schedule(); } return function () { return clearTimeout(handle); }; }, [paused, runImmediately]); } export default useInterval;