import { useRef, useState, useEffect } from 'react'; interface TimeLeft { secondsLeft: string; minutesLeft: string; hoursLeft: string; daysLeft: string; isOver: boolean; currentEndDate: number; } const addZero = (value: number): string => value < 10.0 ? `0${value.toString()}` : value.toString(); const formatTime = (time: number, hasZeroes: boolean): string => hasZeroes ? addZero(time) : time.toString(); const calculateTimeLeft = (endDate: number, hasZeroes: boolean): TimeLeft => { const distance = endDate - Date.now(); const seconds = Math.floor(distance / 1000.0); const minutes = Math.floor(seconds / 60.0); const hours = Math.floor(minutes / 60.0); const days = Math.floor(hours / 24.0); return { secondsLeft: formatTime(Math.floor(seconds % 60.0), hasZeroes), minutesLeft: formatTime(Math.floor(minutes % 60.0), hasZeroes), hoursLeft: formatTime(Math.floor(hours % 24.0), hasZeroes), daysLeft: formatTime(days % 365.0, hasZeroes), isOver: Date.now() > endDate, currentEndDate: endDate, }; }; export type UseTimer = [TimeLeft, React.Dispatch>]; export const useTimer = ( countdownDate: number, hasZeroes: boolean, ): UseTimer => { const animationFrameId = useRef(0); const [endDate, setEndDate] = useState(countdownDate); const [timeLeft, setTimeLeft] = useState( calculateTimeLeft(endDate, hasZeroes), ); useEffect(() => { animationFrameId.current = requestAnimationFrame(() => setTimeLeft(calculateTimeLeft(endDate, hasZeroes)), ); return (): void => cancelAnimationFrame(animationFrameId.current); }, [endDate, timeLeft, hasZeroes]); return [timeLeft, setEndDate]; }; export default useTimer;