- 前端
【免费下载链接】usehooks-ts
React hook library, ready to use, written in Typescript.
useCountdown是 usehooks-ts 提供的一个简单、开箱即用的倒计时自定义 Hook,支持递增与递减两种方向,并在到达指定停止值(countStop)时自动停下。本文以 useCountdown 官方文档 为主体,结合仓库源码与测试用例,带你掌握新版本 API 的全部参数、返回的控制器方法、底层实现原理以及可直接复制的实战示例。
一、概览:一个 Hook 搞定倒计时
倒计时是前端最常见的交互需求之一:验证码发送后的 60 秒重发倒计时、限时活动的剩余时间、游戏或测验的计时……手写这类逻辑通常要同时管理setInterval的创建与清理、计数状态、开始/停止开关,代码容易散落且难以复用。
useCountdown把这一切封装进一个 Hook 中。它基于useCounter、useBoolean、useInterval三个基础 Hook 组合而成,调用后返回当前计数值与三个控制器方法(开始、停止、重置),开发者无需关心定时器的生命周期管理。
文档明确指出:
A simple countdown implementation. Support increment and decrement.
即:一个简单的倒计时实现,支持递增与递减。
新版本接口变更(重要)
useCountdown.md开头特别提示:新版本 useCountdown 将在下一个大版本中废弃旧版本("The new useCountdown is deprecating the old one on the next major version")。新旧版本在调用参数上存在映射关系,迁移时需注意:
| 新版本参数 | 旧版本参数 | 说明 |
|---|---|---|
countStart | seconds | 倒计时起始数值 |
intervalMs | interval | 每次变化的间隔(毫秒) |
isIncrement | (新增) | 是否递增,默认false表示递减 |
countStop | (新增) | 停止数值,到达后自动停止 |
新版本额外支持countStop与isIncrement,并会在计数值到达countStop时自动停止("Will stop when atcountStop")。
二、参数详解:CountdownOptions
从 useCountdown.ts 的源码类型定义可以看出,useCountdown接收一个对象参数CountdownOptions:
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 }各参数说明:
| 参数 | 类型 | 必填 | 默认值 | 含义 |
|---|---|---|---|---|
countStart | number | 是 | — | 倒计时的起始数值,也是返回值count的初始值 |
intervalMs | number | 否 | 1000 | 每次递增/递减的间隔,单位毫秒 |
isIncrement | boolean | 否 | false | 为true时递增计数,为false(默认)时递减计数 |
countStop | number | 否 | 0 | 倒计时的停止数值,到达后自动停止;传入-Infinity可实现无限递减 |
需要特别关注的两个细节(源码 JSDoc 与测试均可印证):
countStop的默认值是0:递减模式下,计数到0时自动停止,这也是最常见的验证码倒计时场景。countStop传入-Infinity表示"递减到永远":即不设置停止边界,计数会一直递减下去。
三、返回值:CountdownControllers
useCountdown返回一个元组[number, CountdownControllers],第一个元素是当前计数值,第二个元素是三个控制器方法(源码见 useCountdown.ts):
type CountdownControllers = { /** Start the countdown. */ startCountdown: () => void /** Stop the countdown. */ stopCountdown: () => void /** Reset the countdown. */ resetCountdown: () => void }| 方法 | 作用 |
|---|---|
startCountdown | 启动倒计时,开始按intervalMs周期递增/递减 |
stopCountdown | 停止倒计时(保留当前计数值) |
resetCountdown | 停止倒计时并将计数重置为countStart初始值 |
典型解构用法:
const [count, { startCountdown, stopCountdown, resetCountdown }] = useCountdown({ countStart: 10, intervalMs: 1000, isIncrement: false, })这正是 useCountdown.ts 中 JSDoc 给出的官方示例形态。
四、基础实战示例:可直接运行的倒计时组件
仓库提供了完整可运行的演示组件 useCountdown.demo.tsx,它演示了「从 60 开始递减 + 可动态调整间隔 + 开始/停止/重置」的完整用法:
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> ) }这个示例展示了两个关键点:
intervalMs可以动态改变:通过useState维护间隔值,将其传入 Hook 后,即使倒计时运行中修改间隔,useInterval也会以新间隔重建定时器(原理见下文)。- 三按钮控制模型:
start/stop/reset分别对应startCountdown/stopCountdown/resetCountdown,覆盖了倒计时组件的全部控制需求。
在验证码场景中,只需将按钮替换为"发送验证码",并在count === 0时禁用按钮即可。
五、源码原理:三个基础 Hook 的组合
useCountdown之所以实现简洁,是因为它把状态、开关、定时器三件事分别委托给了仓库内已有的基础 Hook。完整实现见 useCountdown.ts:
export function useCountdown({ countStart, countStop = 0, intervalMs = 1000, isIncrement = false, }: CountdownOptions): [number, CountdownControllers] { const { count, increment, decrement, reset: resetCounter } = useCounter(countStart) const { value: isCountdownRunning, setTrue: startCountdown, setFalse: stopCountdown } = useBoolean(false) 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 }] }5.1 计数状态:来自useCounter
计数值由useCounter(countStart)提供。在 useCounter.ts 中,count通过useState(initialValue ?? 0)初始化,并封装了increment(x => x + 1)、decrement(x => x - 1)与reset(重置为初始值)等稳定回调。useCountdown只借用其中的count、increment、decrement、reset,因此计数值的变化始终遵循"每次 ±1"的步长。
5.2 运行开关:来自useBoolean
倒计时的启停由useBoolean(false)驱动,useBoolean.ts 提供了setTrue/setFalse等工具方法。在useCountdown中:
setTrue直接暴露为startCountdown;setFalse直接暴露为stopCountdown;- 布尔值
isCountdownRunning作为定时器的"开关信号"。
5.3 定时器:来自useInterval
核心循环由useInterval驱动:
useInterval(countdownCallback, isCountdownRunning ? intervalMs : null)useInterval.ts 的语义是:delay为null时不创建定时器(相当于清除),为数字时按毫秒周期执行回调。因此:
- 未调用
startCountdown时,isCountdownRunning为false,delay为null,定时器不存在; - 调用
startCountdown后,delay变为intervalMs,定时器启动; - 停止时
delay回到null,定时器被清理。
值得一提的是,useInterval内部用useRef保存最新回调,因此countdownCallback即使每次渲染都变化,也不会导致定时器频繁重建(除非delay变化)。
5.4 停止逻辑:到达countStop自动刹车
countdownCallback在每次定时器触发时首先检查:
if (count === countStop) { stopCountdown() return }当计数值等于countStop时,立即调用stopCountdown()关闭定时器并终止后续的递增/递减,从而把计数"钉"在停止值上。这就是文档所述 "Will stop when atcountStop" 的实现位置。
六、边界行为与测试验证
仓库中的 useCountdown.test.ts 使用 Vitest 的假定时器(vitest.useFakeTimers())系统验证了上述全部行为,也是理解 Hook 语义的最佳"活文档":
| 测试场景 | 配置 | 验证行为 |
|---|---|---|
| 返回可调用函数 | countStart: 60, intervalMs: 500 | 初始值60,三个控制器均为函数 |
| 递增模式 | isIncrement: true, intervalMs: 500 | 推进1000ms后计数值为62(每次 +1) |
| 递减模式 | 默认参数 | 推进1000ms后计数值为58(每次 -1) |
自定义intervalMs | intervalMs: 500 | 推进500ms后计数值为59 |
默认countStop: 0 | countStart: 60 | 推进60 * 1000ms后停在0,继续推进仍为0 |
自定义countStop | countStart: 60, countStop: 30 | 推进30 * 1000ms后停在30,不再变化 |
| 停止功能 | 默认参数 | start后推进2000ms计数58;stop后推进3000ms仍为58 |
| 反向递增停止 | countStart: 10, countStop: 20, isIncrement: true | 递增到20后自动停止,继续推进仍为20 |
| 重置功能 | 默认参数 | 递减后调用resetCountdown,计数值恢复为60 |
其中「反向倒计时」用例(countStart: 10→countStop: 20,递增模式)证明:countStop不要求小于countStart,配合isIncrement: true可以实现"从 10 数到 20"的正向计时,停止逻辑对递增/递减方向一视同仁。
七、与其他 Hook 的关系
useCountdown的文档页列出了四个密切相关、可组合使用的 Hook(对应源码均位于仓库 src 目录 下):
useBoolean():提供setTrue/setFalse/toggle等布尔状态工具,是useCountdown启停开关的底层来源;useToggle():基于useBoolean的布尔切换封装,适合管理"是否显示倒计时"等 UI 状态;useCounter():提供increment/decrement/reset/setCount,是计数值的底层来源;useInterval():以delay为null即清除定时器的方式驱动倒计时循环。
理解这层组合关系后,你既可以直接使用useCountdown,也可以在特殊需求下(例如需要setCount直接跳转到任意值)退回到useCounter+useInterval自行组装。
八、使用建议与注意事项
- 新老 API 迁移:如果你正在使用旧版
useCountdown({ seconds, interval }),请按第一节的映射表迁移为countStart/intervalMs,并注意旧版没有countStop与isIncrement。 - 无限递减:需要无边界递减时,显式传入
countStop: -Infinity(源码 JSDoc 明确提示 "Pass-Infinityto decrease forever")。 - 递增方向:默认
isIncrement: false是递减;做"正向计时"(如从 0 数到目标值)时记得设置isIncrement: true并给出大于countStart的countStop。 - 动态间隔:
intervalMs支持在运行中动态修改,示例组件(useCountdown.demo.tsx)演示了通过useState驱动间隔值变化的做法。 - 开始前计数不动:未调用
startCountdown之前,count始终保持countStart,因为定时器在isCountdownRunning为false时根本不存在。 - 只在到达停止值时自动停止:手动
stopCountdown不会重置计数,如需"停止并归位"请调用resetCountdown(其实现为stopCountdown()+resetCounter(),见 useCountdown.ts)。
结语
useCountdown用极小的 API 表面(一个配置对象 + 一个元组返回值)覆盖了倒计时的全部核心诉求:递减、递增、自动停止、手动启停与重置。通过阅读 useCountdown.ts、useCountdown.test.ts 与 useCountdown.demo.tsx 三份文件,你不仅能直接上手使用,还能看清它如何优雅地组合useCounter、useBoolean、useInterval三个基础 Hook——这种"组合式"设计正是 usehooks-ts 这类 React Hook 库值得借鉴的架构思路。
- 前端
【免费下载链接】usehooks-ts
React hook library, ready to use, written in Typescript.
相关推荐
ahooks useCountDown 倒计时 Hook 完全指南:API 详解、毫秒精度与源码实现剖析
ahooks useCountDown 倒计时 Hook 完全指南:API 详解、毫秒精度与源码实现剖析 useCountDown 是 ahooks(位于 pa
前端ahooks useCountDown 倒计时 Hook 完全指南:API 详解、精度陷阱与实战场景
ahooks useCountDown 倒计时 Hook 完全指南:API 详解、精度陷阱与实战场景 导读 useCountDown 是 ahooks(GitH
前端usehooks-ts useScreen Hook 完整指南:实时追踪 window.screen 屏幕对象,兼容防抖与 SSR
usehooks ts useScreen Hook 完整指南:实时追踪 window.screen 屏幕对象,兼容防抖与 SSR useScreen 是 us
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考