如何在React函数组件中使用useEffect解决倒计时运行1秒就停止的问题
React 倒计时功能异常修复方案
问题根因
你的代码出现1秒后停止的核心原因有两个:
- 组件内部声明的
timer变量属于函数作用域变量,每次组件因为state更新重渲染时都会被重置,无法持久保存定时器ID,后续也无法正确清除无效定时器 setInterval回调形成了闭包,捕获的永远是第一次执行startTimer时的timerTime初始值,计算后很快触发终止逻辑,无法实现持续倒计时
修复实现
我们可以通过useEffect监听倒计时启动状态,结合useRef存储定时器ID避免闭包问题,完整实现代码如下:
import { useState, useEffect, useRef } from 'react'; function InputTimer() { const [timerOn, setTimerOn] = useState(false); // 可根据需求修改默认倒计时时长,或绑定输入框的值 const [timerTime, setTimerTime] = useState(10); // 用ref存定时器ID,跨渲染周期保持值不变 const timerRef = useRef(null); const startTimer = () => { // 先清理之前可能存在的定时器,避免重复启动冲突 if (timerRef.current) clearInterval(timerRef.current); // 可替换为用户自定义输入的倒计时时长 setTimerTime(10); setTimerOn(true); } // 定时器逻辑托管到useEffect,依赖倒计时启动状态 useEffect(() => { if (timerOn) { timerRef.current = setInterval(() => { // 使用state的函数式更新写法,直接获取上一轮最新的时间值,规避闭包旧值问题 setTimerTime(prevTime => { const newTime = prevTime - 1; if (newTime <= 0) { clearInterval(timerRef.current); setTimerOn(false); alert("Countdown Ended"); return 0; } return newTime; }); }, 1000); } // 组件卸载/依赖变更时自动清理定时器,避免内存泄漏 return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, [timerOn]); // 基础UI渲染示例,可根据需求自行扩展 return ( <div> <div>剩余时间:{timerTime}秒</div> <button onClick={startTimer} disabled={timerOn}>启动倒计时</button> </div> ) }
核心优化点
- 用
useRef存储定时器ID,保证跨渲染周期可以访问到同一个定时器实例,避免重渲染丢失ID - state更新使用函数式写法,直接拿到上一轮的最新状态值,彻底规避闭包捕获旧值的问题
- useEffect返回清理函数,在组件卸载、倒计时状态变更时自动清理定时器,避免内存泄漏和异常触发
内容的提问来源于stack exchange,提问作者Natasha Martinez
相关产品推荐
相关产品推荐

