React中使用setInterval/clearInterval实现浴缸水位控制异常问题咨询
问题根因分析
第一版代码仅触发一次的原因
- 定时器ID存储在普通局部变量
fillTub/drainTub中,React组件每次状态更新触发重渲染时,局部变量都会被初始化重置,无法保留之前的定时器ID,clearInterval无法匹配到要清除的定时器实例。 - 定时器回调存在闭包陷阱:回调函数捕获的是首次渲染时的
waterLevel值(永远为0),执行一次+1操作后,后续判断始终读取旧值,逻辑直接卡住。
第二版代码定时器无法停止、水位异常的原因
- 仍存在闭包陷阱问题:
setInterval回调中引用的waterLevel和intervalId都是点击按钮创建定时器那一刻的状态快照,不是实时更新的最新值。比如初始点击上升按钮时waterLevel为0,后续所有回调执行时读取的waterLevel永远是0,waterLevel>4的判断永远不成立,定时器自然无法清除。 - 缺少定时器互斥逻辑:反复点击不同按钮会生成多个定时器同时运行,水位逻辑完全混乱。
修复后可用代码
import React, { useState, useRef, useEffect } from "react"; const App = () => { const [waterLevel, setWaterLevel] = useState(0); // 用useRef存储定时器ID,重渲染不会重置 const timerRef = useRef(null); // 组件卸载时清除定时器,避免内存泄漏 useEffect(() => { return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, []); const increaseWaterLevel = () => { // 先清除已有定时器,避免多个定时器同时运行 if (timerRef.current) clearInterval(timerRef.current); timerRef.current = setInterval(() => { setWaterLevel(prev => { if (prev >= 5) { clearInterval(timerRef.current); timerRef.current = null; return prev; } return prev + 1; }); }, 2000); }; const decreaseWaterLevel = () => { if (timerRef.current) clearInterval(timerRef.current); timerRef.current = setInterval(() => { setWaterLevel(prev => { if (prev <= 0) { clearInterval(timerRef.current); timerRef.current = null; return prev; } return prev - 1; }); }, 2000); }; return ( <div> <div>Bathtub 当前水位:{waterLevel}/5</div> <button onClick={increaseWaterLevel}>increaseWaterLevel</button> <button onClick={decreaseWaterLevel}>decreaseWaterLevel</button> </div> ); }; export default App;
内容的提问来源于stack exchange,提问作者Gareth
相关产品推荐
相关产品推荐

