useCountdown()

IMPORTANT: The new useCountdown is deprecating the old one on the next major version.

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:

The Hook

1// TODO: example and test
2import { useCallback } from 'react'
3
4import { useBoolean, useCounter, useInterval } from 'usehooks-ts'
5
6// Old interface IN & OUT
7interface UseCountdownType {
8 seconds: number
9 interval: number
10 isIncrement?: boolean
11}
12interface CountdownHelpers {
13 start: () => void
14 stop: () => void
15 reset: () => void
16}
17
18// New interface IN & OUT
19interface CountdownOption {
20 countStart: number
21 intervalMs?: number
22 isIncrement?: boolean
23 countStop?: number
24}
25interface CountdownControllers {
26 startCountdown: () => void
27 stopCountdown: () => void
28 resetCountdown: () => void
29}
30
31/**
32 *
33 * @param {UseCountdownType} countdownOption
34 * @param {number} countdownOption.seconds the countdown's number, generally time seconds
35 * @param {number} countdownOption.interval the countdown's interval, milliseconds
36 * @param {?boolean} countdownOption.isIncrement false by default, determine the countdown is increment, otherwise is decrement
37 * @returns [counter, CountdownControllers]
38 *
39 * @deprecated new useCountdown interface is already available (see https://usehooks-ts.com/react-hook/use-countdown), the old version will retire on usehooks-ts@3
40 */
41function useCountdown(
42 countdownOption: UseCountdownType,
43): [number, CountdownHelpers]
44
45/**
46 * New interface with default value
47 *
48 * @param {CountdownOption} countdownOption
49 * @param {number} countdownOption.countStart - the countdown's starting number, initial value of the returned number.
50 * @param {?number} countdownOption.countStop - `0` by default, the countdown's stopping number. Pass `-Infinity` to decrease forever.
51 * @param {?number} countdownOption.intervalMs - `1000` by default, the countdown's interval, in milliseconds.
52 * @param {?boolean} countdownOption.isIncrement - `false` by default, true if the countdown is increment.
53 * @returns [counter, CountdownControllers]
54 */
55function useCountdown(
56 countdownOption: CountdownOption,
57): [number, CountdownControllers]
58
59function useCountdown(
60 countdownOption: UseCountdownType | CountdownOption,
61): [number, CountdownHelpers | CountdownControllers] {
62 /**
63 * Use to determine the the API call is a deprecated version.
64 */
65 let isDeprecated = false
66
67 let countStart,
68 intervalMs,
69 isIncrement: boolean | undefined,
70 countStop: number | undefined
71
72 if ('seconds' in countdownOption) {
73 console.warn(
74 '[useCountdown:DEPRECATED] new interface is already available (see https://usehooks-ts.com/react-hook/use-countdown), the old version will retire on usehooks-ts@3.',
75 )
76
77 isDeprecated = true
78 countStart = countdownOption.seconds
79 intervalMs = countdownOption.interval
80 isIncrement = countdownOption.isIncrement
81 } else {
82 // eslint-disable-next-line @typescript-eslint/no-extra-semi
83 ;({ countStart, intervalMs, isIncrement, countStop } = countdownOption)
84 }
85
86 // default values
87 intervalMs = intervalMs ?? 1000
88 isIncrement = isIncrement ?? false
89 countStop = countStop ?? 0
90
91 const {
92 count,
93 increment,
94 decrement,
95 reset: resetCounter,
96 } = useCounter(countStart)
97
98 /**
99 * Note: used to control the useInterval
100 * running: If true, the interval is running
101 * start: Should set running true to trigger interval
102 * stop: Should set running false to remove interval
103 */
104 const {
105 value: isCountdownRunning,
106 setTrue: startCountdown,
107 setFalse: stopCountdown,
108 } = useBoolean(false)
109
110 /**
111 * Will set running false and reset the seconds to initial value
112 */
113 const resetCountdown = () => {
114 stopCountdown()
115 resetCounter()
116 }
117
118 const countdownCallback = useCallback(() => {
119 if (count === countStop) {
120 stopCountdown()
121 return
122 }
123
124 if (isIncrement) {
125 increment()
126 } else {
127 decrement()
128 }
129 }, [count, countStop, decrement, increment, isIncrement, stopCountdown])
130
131 useInterval(countdownCallback, isCountdownRunning ? intervalMs : null)
132
133 return isDeprecated
134 ? [
135 count,
136 {
137 start: startCountdown,
138 stop: stopCountdown,
139 reset: resetCountdown,
140 } as CountdownHelpers,
141 ]
142 : [
143 count,
144 {
145 startCountdown,
146 stopCountdown,
147 resetCountdown,
148 } as CountdownControllers,
149 ]
150}
151
152export default useCountdown

Usage

1import { ChangeEvent, useState } from 'react'
2
3import { useCountdown } from 'usehooks-ts'
4
5export default function Component() {
6 const [intervalValue, setIntervalValue] = useState<number>(1000)
7 const [count, { startCountdown, stopCountdown, resetCountdown }] =
8 useCountdown({
9 countStart: 60,
10 intervalMs: intervalValue,
11 })
12
13 const handleChangeIntervalValue = (event: ChangeEvent<HTMLInputElement>) => {
14 setIntervalValue(Number(event.target.value))
15 }
16 return (
17 <div>
18 <p>Count: {count}</p>
19
20 <input
21 type="number"
22 value={intervalValue}
23 onChange={handleChangeIntervalValue}
24 />
25 <button onClick={startCountdown}>start</button>
26 <button onClick={stopCountdown}>stop</button>
27 <button onClick={resetCountdown}>reset</button>
28 </div>
29 )
30}

Edit on CodeSandbox

See a way to make this page better?
Edit there »