Skip to main content

All hooks

useCountdown

Custom hook that manages countdown.


A simple countdown implementation. Support increment and decrement.

NEW VERSION: A simple countdown implementation. Accepts countStop(new), countStart (was seconds), intervalMs(was interval) and isIncrement as keys of the call argument. Support increment and decrement. Will stop when at countStop.

Related hooks:

Usage

import { useState } from 'react'
import type { ChangeEvent } from 'react'

import { useCountdown } from './useCountdown'

export default function Component() {
  const [intervalValue, setIntervalValue] = useState<number>(1000)
  const [count, { startCountdown, stopCountdown, resetCountdown }] = useCountdown({
    countStart: 60,
    intervalMs: intervalValue,
  })

  const handleChangeIntervalValue = (event: ChangeEvent<HTMLInputElement>) => {
    setIntervalValue(Number(event.target.value))
  }
  return (
    <div>
      <p>Count: {count}</p>

      <input type="number" value={intervalValue} onChange={handleChangeIntervalValue} />
      <button onClick={startCountdown}>start</button>
      <button onClick={stopCountdown}>stop</button>
      <button onClick={resetCountdown}>reset</button>
    </div>
  )
}

API

function useCountdown(countdownOptions: CountdownOptions): unknown

Custom hook that manages countdown.

Parameters

NameTypeDefault valueDescription
countdownOptionsCountdownOptions-The countdown's options.

Returns

An array containing the countdown's count and its controllers.

Type declaration

CountdownControllers

The countdown's controllers.

NameTypeDescription
resetCountdown() => voidReset the countdown.
startCountdown() => voidStart the countdown.
stopCountdown() => voidStop the countdown.

CountdownOptions

The countdown's options.

NameTypeDescription
countStartnumberThe countdown's starting number, initial value of the returned number.
countStopnumberThe countdown's stopping number. Pass -Infinity to decrease forever.
intervalMsnumberThe countdown's interval, in milliseconds.
isIncrementbooleanTrue if the countdown is increment.

Hook

import { useCallback } from 'react'

import { useBoolean } from '../useBoolean'
import { useCounter } from '../useCounter'
import { useInterval } from '../useInterval'

/** The countdown's options. */
export type CountdownOptions = {
  /** The countdown's starting number, initial value of the returned number. */
  countStart: number

  /**
   * The countdown's interval, in milliseconds.
   * @default 1000
   */
  intervalMs?: number
  /**
   * True if the countdown is increment.
   * @default false
   */
  isIncrement?: boolean

  /**
   * The countdown's stopping number. Pass `-Infinity` to decrease forever.
   * @default 0
   */
  countStop?: number
}

/** The countdown's controllers. */
export type CountdownControllers = {
  /** Start the countdown. */
  startCountdown: () => void
  /** Stop the countdown. */
  stopCountdown: () => void
  /** Reset the countdown. */
  resetCountdown: () => void
}

/**
 * Custom hook that manages countdown.
 * @param {CountdownOptions} countdownOptions - The countdown's options.
 * @returns {[number, CountdownControllers]} An array containing the countdown's count and its controllers.
 * @public
 * @see [Documentation](https://usehooks-ts.com/react-hook/use-countdown)
 * @example
 * ```tsx
 * const [counter, { start, stop, reset }] = useCountdown({
 *   countStart: 10,
 *   intervalMs: 1000,
 *   isIncrement: false,
 * });
 * ```
 */
export function useCountdown({
  countStart,
  countStop = 0,
  intervalMs = 1000,
  isIncrement = false,
}: CountdownOptions): [number, CountdownControllers] {
  const {
    count,
    increment,
    decrement,
    reset: resetCounter,
  } = useCounter(countStart)

  /*
   * Note: used to control the useInterval
   * running: If true, the interval is running
   * start: Should set running true to trigger interval
   * stop: Should set running false to remove interval.
   */
  const {
    value: isCountdownRunning,
    setTrue: startCountdown,
    setFalse: stopCountdown,
  } = useBoolean(false)

  // Will set running false and reset the seconds to initial value.
  const resetCountdown = useCallback(() => {
    stopCountdown()
    resetCounter()
  }, [stopCountdown, resetCounter])

  const countdownCallback = useCallback(() => {
    if (count === countStop) {
      stopCountdown()
      return
    }

    if (isIncrement) {
      increment()
    } else {
      decrement()
    }
  }, [count, countStop, decrement, increment, isIncrement, stopCountdown])

  useInterval(countdownCallback, isCountdownRunning ? intervalMs : null)

  return [count, { startCountdown, stopCountdown, resetCountdown }]
}