☰
usehooks-ts `useCountdown` 倒计时 Hook 完整指南:参数、源码原理与实战示例
2026/9/28 11:52:09 网站建设 项目流程
  • 前端

【免费下载链接】usehooks-ts

React hook library, ready to use, written in Typescript.

项目地址:https://gitcode.com/gh_mirrors/us/usehooks-ts
点击查看免费下载

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")。新旧版本在调用参数上存在映射关系,迁移时需注意:

新版本参数旧版本参数说明
countStartseconds倒计时起始数值
intervalMsinterval每次变化的间隔(毫秒)
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 }

各参数说明:

参数类型必填默认值含义
countStartnumber是—倒计时的起始数值,也是返回值count的初始值
intervalMsnumber否1000每次递增/递减的间隔,单位毫秒
isIncrementboolean否false为true时递增计数,为false(默认)时递减计数
countStopnumber否0倒计时的停止数值,到达后自动停止;传入-Infinity可实现无限递减

需要特别关注的两个细节(源码 JSDoc 与测试均可印证):

  1. countStop的默认值是0:递减模式下,计数到0时自动停止,这也是最常见的验证码倒计时场景。
  2. 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)
自定义intervalMsintervalMs: 500推进500ms后计数值为59
默认countStop: 0countStart: 60推进60 * 1000ms后停在0,继续推进仍为0
自定义countStopcountStart: 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自行组装。

八、使用建议与注意事项

  1. 新老 API 迁移:如果你正在使用旧版useCountdown({ seconds, interval }),请按第一节的映射表迁移为countStart/intervalMs,并注意旧版没有countStop与isIncrement。
  2. 无限递减:需要无边界递减时,显式传入countStop: -Infinity(源码 JSDoc 明确提示 "Pass-Infinityto decrease forever")。
  3. 递增方向:默认isIncrement: false是递减;做"正向计时"(如从 0 数到目标值)时记得设置isIncrement: true并给出大于countStart的countStop。
  4. 动态间隔:intervalMs支持在运行中动态修改,示例组件(useCountdown.demo.tsx)演示了通过useState驱动间隔值变化的做法。
  5. 开始前计数不动:未调用startCountdown之前,count始终保持countStart,因为定时器在isCountdownRunning为false时根本不存在。
  6. 只在到达停止值时自动停止:手动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.

项目地址:https://gitcode.com/gh_mirrors/us/usehooks-ts
点击查看免费下载
上一篇:SOCD Cleaner终极指南:彻底解决游戏键盘方向冲突的免费开源神器
下一篇:5分钟掌握PUBG罗技鼠标宏:告别压枪烦恼的终极指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询