Skip to content

useTimer

React hook to create a timer functionality.

Add the hook via the CLI:

sh
npx @novajslabs/cli add useTimer
sh
npx @novajslabs/cli add useTimer
sh
pnpm dlx @novajslabs/cli add useTimer

Or copy and paste the code into your project:

ts
import { useState, useEffect } from "react";

const parseTime = (time: string) => {
  const splitTime = time.split(":");

  const [days, hours, minutes, seconds] = splitTime.map((value) =>
    Number(value)
  );

  return { days, hours, minutes, seconds };
};

const addLeadingZero = (digit: number): string => {
  let timeStr = "";

  digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);

  return timeStr;
};

interface Timer {
  current: string;
  isPaused: boolean;
  isOver: boolean;
  currentDays: number;
  currentHours: number;
  currentMinutes: number;
  currentSeconds: number;
  elapsedSeconds: number;
  remainingSeconds: number;
  pause: () => void;
  play: () => void;
  reset: () => void;
  togglePause: () => void;
}

export const useTimer = (startTime: string): Timer => {
  const { days, hours, minutes, seconds } = parseTime(startTime);
  const [time, setTime] = useState({ days, hours, minutes, seconds });
  const [paused, setPaused] = useState(false);
  const divider = ":";
  const [isOver, setIsOver] = useState(false);

  useEffect(() => {
    if (paused) {
      return;
    }

    const interval = setInterval(() => {
      setTime((prev) => {
        let d = prev.days;
        let h = prev.hours;
        let m = prev.minutes;
        let s = prev.seconds;

        if (s - 1 < 0) {
          s = 59;
          if (m - 1 < 0) {
            m = 59;
            if (h - 1 < 0) {
              h = 23;
              if (d - 1 >= 0) {
                d--;
              }
            } else {
              h--;
            }
          } else {
            m--;
          }
        } else {
          s--;
        }

        return { days: d, hours: h, minutes: m, seconds: s };
      });
    }, 1000);

    if (
      time.seconds === 0 &&
      time.minutes === 0 &&
      time.hours === 0 &&
      time.days === 0
    ) {
      setIsOver(true);
      clearInterval(interval);
      return;
    }

    return () => clearInterval(interval);
  }, [days, hours, minutes, seconds, time, paused]);

  return {
    current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(
      time.hours
    )}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(
      time.seconds
    )}`,
    isPaused: paused,
    isOver,
    currentDays: time.days,
    currentHours: time.hours,
    currentMinutes: time.minutes,
    currentSeconds: time.seconds,
    elapsedSeconds:
      days * 86400 +
      hours * 3600 +
      minutes * 60 +
      seconds -
      (time.days * 86400 +
        time.hours * 3600 +
        time.minutes * 60 +
        time.seconds),
    remainingSeconds:
      time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,
    pause: () => setPaused(true),
    play: () => setPaused(false),
    reset: () => {
      setIsOver(false);
      setTime({ days, hours, minutes, seconds });
    },
    togglePause: () => {
      setPaused(!paused);
    },
  };
};
js
import { useState, useEffect } from "react";

const parseTime = (time) => {
  const splitTime = time.split(":");

  const [days, hours, minutes, seconds] = splitTime.map((value) =>
    Number(value)
  );

  return { days, hours, minutes, seconds };
};

const addLeadingZero = (digit) => {
  let timeStr = "";

  digit % 10 === digit ? (timeStr += `0${digit}`) : (timeStr += `${digit}`);

  return timeStr;
};

export const useTimer = (startTime) => {
  const { days, hours, minutes, seconds } = parseTime(startTime);
  const [time, setTime] = useState({ days, hours, minutes, seconds });
  const [paused, setPaused] = useState(false);
  const divider = ":";
  const [isOver, setIsOver] = useState(false);

  useEffect(() => {
    if (paused) {
      return;
    }

    const interval = setInterval(() => {
      setTime((prev) => {
        let d = prev.days;
        let h = prev.hours;
        let m = prev.minutes;
        let s = prev.seconds;

        if (s - 1 < 0) {
          s = 59;
          if (m - 1 < 0) {
            m = 59;
            if (h - 1 < 0) {
              h = 23;
              if (d - 1 >= 0) {
                d--;
              }
            } else {
              h--;
            }
          } else {
            m--;
          }
        } else {
          s--;
        }

        return { days: d, hours: h, minutes: m, seconds: s };
      });
    }, 1000);

    if (
      time.seconds === 0 &&
      time.minutes === 0 &&
      time.hours === 0 &&
      time.days === 0
    ) {
      setIsOver(true);
      clearInterval(interval);
      return;
    }

    return () => clearInterval(interval);
  }, [days, hours, minutes, seconds, time, paused]);

  return {
    current: `${addLeadingZero(time.days)}${divider}${addLeadingZero(
      time.hours
    )}${divider}${addLeadingZero(time.minutes)}${divider}${addLeadingZero(
      time.seconds
    )}`,
    isPaused: paused,
    isOver,
    currentDays: time.days,
    currentHours: time.hours,
    currentMinutes: time.minutes,
    currentSeconds: time.seconds,
    elapsedSeconds:
      days * 86400 +
      hours * 3600 +
      minutes * 60 +
      seconds -
      (time.days * 86400 +
        time.hours * 3600 +
        time.minutes * 60 +
        time.seconds),
    remainingSeconds:
      time.days * 86400 + time.hours * 3600 + time.minutes * 60 + time.seconds,
    pause: () => setPaused(true),
    play: () => setPaused(false),
    reset: () => {
      setIsOver(false);
      setTime({ days, hours, minutes, seconds });
    },
    togglePause: () => {
      setPaused(!paused);
    },
  };
};

Requirements

React 16.8 or higher

Parameters

startTime required

Type: string

Represents the initial value of the timer in the format dd:hh:mm:ss.

Return values

current

Type: string

The current value of the timer.

isPaused

Type: boolean

Represents whether the timer is paused (true) or not (false).

isOver

Type: boolean

Represents whether the timer has ended (true) or not (false).

currentDays

Type: number

The current value of days on the timer.

currentHours

Type: number

The current value of hours on the timer.

currentMinutes

Type: number

The current value of minutes on the timer.

currentSeconds

Type: number

The current value of the seconds in the timer.

elapsedSeconds

Type: number

The number of seconds that have passed since the start of the timer.

remainingSeconds

Type: number

The number of seconds left until the timer ends.

pause

Type: function

Pause the timer.

play

Type: function

Play the timer.

reset

Type: function

Reset the timer.

togglePause

Type: function

Toggle between pausing and playing the timer.

Example

tsx
import { useTimer } from "./hooks/useTimer";

const App = () => {
  const {
    current,
    isPaused,
    isOver,
    currentDays,
    currentHours,
    currentMinutes,
    currentSeconds,
    elapsedSeconds,
    remainingSeconds,
    pause,
    play,
    reset,
    togglePause,
  } = useTimer("00:00:00:05");

  return (
    <div>
      <p>Counter value: {current}</p>
      <p>Is the counter paused? {isPaused ? "Yes" : "No"}</p>
      <p>Has the counter over? {isOver ? "Yes" : "No"}</p>
      <p>Current days: {currentDays}</p>
      <p>Current hours: {currentHours}</p>
      <p>Current minutes: {currentMinutes}</p>
      <p>Current seconds: {currentSeconds}</p>
      <p>Elapsed seconds: {elapsedSeconds}</p>
      <p>Remaining seconds: {remainingSeconds}</p>
      <button onClick={pause}>Pause</button>
      <button onClick={play}>Play</button>
      <button onClick={reset}>Reset</button>
      <button onClick={togglePause}>Toggle Pause</button>
    </div>
  );
};

export default App;

Use cases

Here are some use cases where this React hook is useful:

  • Implementing a countdown timer for a time-limited quiz or exam in an online learning platform
  • Creating a countdown clock for tracking time-sensitive tasks or deadlines in a project management tool
  • Developing a timer feature for timing cooking recipes or workouts in a fitness app
  • Adding a countdown timer for promotions or special events in an e-commerce website
  • Integrating a timer functionality for time-bound challenges or activities in a gamified learning platform