REACT

useCountdown Hook

A custom React Hook for countdown timer with TypeScript support

ReactHooksTimerCountdownUI

Code

import { useState, useEffect, useRef, useCallback } from 'react';

type CountdownState = {
  seconds: number;
  minutes: number;
  hours: number;
  days: number;
  isRunning: boolean;
  isFinished: boolean;
};

function useCountdown(
  targetDate: Date | number,
  autoStart = true
): {
  state: CountdownState;
  start: () => void;
  pause: () => void;
  reset: () => void;
} {
  const [state, setState] = useState<CountdownState>({
    seconds: 0,
    minutes: 0,
    hours: 0,
    days: 0,
    isRunning: autoStart,
    isFinished: false,
  });
  const intervalRef = useRef<NodeJS.Timeout | null>(null);
  const targetTimestamp = useRef<number>(
    typeof targetDate === 'number' ? targetDate : targetDate.getTime()
  );

  // Calculate remaining time
  const calculateRemainingTime = useCallback(() => {
    const now = Date.now();
    const diff = targetTimestamp.current - now;

    __TOKEN_31__ (diff <= 0) {
      return {
        seconds: 0,
        minutes: 0,
        hours: 0,
        days: 0,
        isFinished: true,
      };
    }

    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
    const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
    const seconds = Math.floor((diff % (1000 * 60)) / 1000);

    return {
      days,
      hours,
      minutes,
      seconds,
      isFinished: false,
    };
  }, []);

  // Update countdown
  const updateCountdown = useCallback(() => {
    const remaining = calculateRemainingTime();
    setState(prev => ({ ...prev, ...remaining }));

    __TOKEN_40__ (remaining.isFinished) {
      pause();
    }
  }, [calculateRemainingTime]);

  // Start countdown
  const start = useCallback(() => {
    __TOKEN_42__ (state.isFinished || intervalRef.current) return;
    setState(prev => ({ ...prev, isRunning: true }));
    intervalRef.current = setInterval(updateCountdown, 1000);
    updateCountdown(); // Immediate update
  }, [state.isFinished, updateCountdown]);

  // Pause countdown
  const pause = useCallback(() => {
    __TOKEN_45__ (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
      setState(prev => ({ ...prev, isRunning: false }));
    }
  }, []);

  // Reset countdown
  const reset = useCallback(() => {
    pause();
    const remaining = calculateRemainingTime();
    setState({
      ...remaining,
      isRunning: autoStart,
      isFinished: remaining.isFinished,
    });
    __TOKEN_48__ (autoStart && !remaining.isFinished) {
      start();
    }
  }, [pause, calculateRemainingTime, autoStart, start]);

  // Initial setup
  useEffect(() => {
    __TOKEN_49__ (autoStart) {
      start();
    } else {
      updateCountdown();
    }

    // Cleanup
    __TOKEN_51__ () => {
      pause();
    };
  }, [autoStart, start, updateCountdown, pause]);

  return { state, start, pause, reset };
}

// Usage example
// const targetDate = new Date();
// targetDate.setDate(targetDate.getDate() + 1); // 1 day from now
// const { state, start, pause, reset } = useCountdown(targetDate);
// <div>
//   <p>{state.days}d {state.hours}h {state.minutes}m {state.seconds}s</p>
//   <button onClick={start} disabled={state.isRunning || state.isFinished}>Start</button>
//   <button onClick={pause} disabled={!state.isRunning}>Pause</button>
//   <button onClick={reset}>Reset</button>
// </div>