React 5秒计时器触发无限重渲染崩溃问题排查与修复求助
React组件无限重渲染问题分析与修复
错误原因
- 渲染阶段触发状态更新:你在组件返回的JSX中直接调用
time()函数,而time()内部包含setMs、setSecs、setDone这些状态更新方法。组件每次渲染都会执行time(),状态更新又会触发组件重新渲染,循环往复形成无限渲染循环,触发React的"Too many re-renders"报错。 - 闭包捕获旧值:
useEffect的依赖数组为空,导致内部定时器回调捕获的done是初始状态false。即使后续通过setDone(true)将状态改为true,定时器回调里的done依然是旧值,会继续执行setMs,进一步加剧重渲染问题。
修复方案
核心思路是把状态更新逻辑从渲染阶段剥离,放到合适的副作用钩子或状态更新回调中,同时修正useEffect的依赖项:
修复后的代码
function App() { const [secs, setSecs] = React.useState(0); const [ms, setMs] = React.useState(0); const [done, setDone] = React.useState(false); const id = React.useRef(); // 定时器逻辑:处理毫秒递增,包含毫秒转秒的逻辑 React.useEffect(() => { id.current = setInterval(() => { if (done) return; setMs(prevMs => { if (prevMs === 100) { setSecs(prevSecs => prevSecs + 1); return 0; } return prevMs + 1; }); }, 10); return () => clearInterval(id.current); }, [done]); // 添加done作为依赖,确保回调能拿到最新状态 // 监听秒数变化,达到5秒时停止定时器 React.useEffect(() => { if (secs === 5) { clearInterval(id.current); setDone(true); } }, [secs]); // 仅负责时间格式化,无状态更新逻辑 const formatTime = () => { const formattedSecs = secs < 10 ? `0${secs}` : secs; const formattedMils = ms < 10 ? `0${ms}` : ms; return `${formattedSecs} : ${formattedMils}`; }; return <div>{formatTime()}</div>; }
关键修复点
- 拆分逻辑:将原来
time()中的状态更新和格式化显示逻辑拆分,formatTime()只做字符串格式化,不再触发状态更新,避免渲染时触发重渲染。 - 移转状态更新:把毫秒转秒的逻辑放到
setMs的函数式更新回调中,确保在状态更新时处理,而非渲染阶段。 - 修正依赖项:给定时器的
useEffect添加done依赖,保证回调能捕获到最新的done状态,避免done为true后定时器仍继续执行。 - 单独监听秒数:用独立的
useEffect监听secs变化,当秒数达到5时停止定时器并标记完成,该逻辑仅在secs变化时执行,不会重复触发。
内容的提问来源于stack exchange,提问作者Montin
相关产品推荐
相关产品推荐

