UNPKG

react-use-timer-hook

Version:

A React hook for managing countdown or count-up timers with pause, reset, and customizable callbacks.

92 lines (91 loc) 2.97 kB
import { isNull } from '@migudevelop/types-utils'; import { useEffect, useRef } from 'react'; import { getTimeFromMilliseconds, getTimeFromSeconds } from './helpers'; import { useTimerReducer } from './useTimerReducer'; const INTERVAL = 1000; // 1 second /** * React hook for a flexible timer supporting countdown, count-up, pause, and granular time tracking. * @param options UseTimerOptions configuration object * @returns Timer state and control methods */ export function useTimer({ time: initialTime = 0, countUp = false, autoStart = false, onFinish, onTick, onReset } = {}) { const finishTime = countUp ? initialTime : 0; const { isRunning, isPaused, pauseStart, totalPauseTime, pauseTime, time, start, pause, pauseTick, reset, tick } = useTimerReducer({ time: countUp ? 0 : initialTime, finishTime, autoStart, countUp }); const intervalRef = useRef(null); const pauseIntervalRef = useRef(null); /** * Timer interval effect: handles ticking when running */ useEffect(() => { if (!isRunning) { if (intervalRef.current) clearInterval(intervalRef.current); return; } intervalRef.current = setInterval(() => { tick(); onTick?.(); }, INTERVAL); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [isRunning, tick, onTick]); /** * Pause interval effect: tracks pause duration when paused */ useEffect(() => { if (isRunning || !isPaused) { if (pauseIntervalRef.current) clearInterval(pauseIntervalRef.current); return; } pauseIntervalRef.current = setInterval(() => { pauseTick(); }, INTERVAL); return () => { if (pauseIntervalRef.current) clearInterval(pauseIntervalRef.current); }; }, [isRunning, pauseTick, isPaused]); /** * Reset effect: resets timer if initialTime changes */ useEffect(() => { reset(); onReset?.(); }, [reset, onReset]); /** * Syncs pause state: if timer is running and pauseStart is set, update totalPauseTime */ useEffect(() => { if (isRunning && isNull(pauseStart)) { start(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isRunning]); /** * Expire callback: calls onFinish when timer completes */ useEffect(() => { if (time === finishTime && !isRunning) { onFinish?.(); } }, [time, onFinish, finishTime, isRunning]); return { ...getTimeFromSeconds(time), isRunning, start, pause, reset, time, totalPauseTime: getTimeFromMilliseconds(totalPauseTime), pauseTime: getTimeFromSeconds(pauseTime), pauseStart }; }