React使用setInterval实现类番茄钟定时器重启后sessionSeconds不更新问题
问题根因
- 闭包陷阱:
sessionSeconds是startTimer的局部变量,setInterval回调形成的闭包只会捕获函数执行时的上下文,后续状态更新后旧闭包无法拿到最新的iswork、work、rest等值 - 定时器引用未持久化:你没有将定时器ID存储在
useRef中,组件重渲染后原有定时器引用会丢失,无法正确清理旧定时器,容易出现多个定时器同时运行的问题 - 变量未定义:判断条件中的
duration没有任何赋值逻辑,实际应该判断sessionSeconds是否小于等于0 - 未实时更新状态:你只在切换时段的时候调用
updateTime,秒数变化时没有更新remainingTime,页面不会实时刷新计时
修复后代码
import { useState, useRef, useEffect } from 'react'; const TimeTracker = () => { const [work, setWork] = useState(25); const [rest, setRest] = useState(5); const [remainingTime, setRemainingTime] = useState(work * 60); const [iswork, setIswork] = useState(true); // 用useRef存储定时器id,持久化引用避免渲染后丢失 const intervalRef = useRef(null); const updateTime = (seconds) => { setRemainingTime(seconds); }; const startTimer = () => { // 先清除已有定时器,避免多定时器并行 if (intervalRef.current) { clearInterval(intervalRef.current); } let sessionSeconds = iswork ? work * 60 : rest * 60; intervalRef.current = setInterval(() => { sessionSeconds--; // 实时更新页面显示的剩余时间 updateTime(sessionSeconds); if (sessionSeconds <= 0) { // 切换工作/休息状态 setIswork(prev => !prev); // 重启计时器进入下一个时段 startTimer(); } }, 1000); }; // 组件卸载时清除定时器,避免内存泄漏 useEffect(() => { return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, []); return ( <div> <p> {/* 可自行添加时间转换逻辑,把秒数转换为时分秒格式展示 */} {remainingTime} </p> <button onClick={startTimer}>start timer</button> </div> ); };
可选优化建议
- 可以把定时器逻辑放到
useEffect中,通过监听iswork的变化自动切换时段,不用手动在定时器回调里调用startTimer - 如果需要新增暂停、重置功能,直接操作
intervalRef.current清除定时器、重置对应状态即可
内容的提问来源于stack exchange,提问作者slawckaJS
相关产品推荐
相关产品推荐

