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

Usage

1import React, { 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 »