You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 5秒计时器触发无限重渲染崩溃问题排查与修复求助

React组件无限重渲染问题分析与修复

错误原因

  1. 渲染阶段触发状态更新:你在组件返回的JSX中直接调用time()函数,而time()内部包含setMs、setSecs、setDone这些状态更新方法。组件每次渲染都会执行time(),状态更新又会触发组件重新渲染,循环往复形成无限渲染循环,触发React的"Too many re-renders"报错。
  2. 闭包捕获旧值: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 02:50:34