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

React useEffect倒计时Windows本地跳秒,在线编辑器正常原因排查

React 18 倒计时跳秒问题的原因与解决方法

问题根源

  • React 18 开发环境的严格模式会主动重复执行组件的副作用逻辑(包括useEffect),以此检测潜在的代码问题。
  • 你的代码中,每次timeRemaining更新都会触发useEffect创建新的setTimeout,初始渲染时严格模式会让useEffect执行两次,导致同时生成两个1秒后触发的定时器。1秒后两个定时器同时调用setTimeRemaining,数值直接从10跳到8,表现为跳过9秒。
  • React 16 没有严格模式的双重执行机制,而Codepen等在线编辑器要么使用React 16,要么未开启严格模式,因此不会出现跳秒问题。

修复方案

方案1:添加副作用清理,避免重复定时器

使用useRef保存定时器ID,在useEffect返回的清理函数中清除上一个定时器,保证同一时间只有一个定时器运行:

function App() {
  const [timeRemaining, setTimeRemaining] = useState(10);
  const timerRef = useRef(null);

  useEffect(() => {
    if (timeRemaining <= 0) return;
    
    timerRef.current = setTimeout(() => {
      setTimeRemaining(time => time - 1);
    }, 1000);

    // 组件卸载或依赖更新时清除定时器
    return () => clearTimeout(timerRef.current);
  }, [timeRemaining]);

  return (
    <div className="App">          
       <h4>Time remaining: {timeRemaining} </h4>
    </div>
  );
}

方案2:改用setInterval优化逻辑

倒计时场景更适合用setInterval,只需在组件挂载时创建一次定时器,逻辑更简洁高效:

function App() {
  const [timeRemaining, setTimeRemaining] = useState(10);
  const intervalRef = useRef(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setTimeRemaining(prev => {
        if (prev <= 0) {
          clearInterval(intervalRef.current);
          return 0;
        }
        return prev - 1;
      });
    }, 1000);

    return () => clearInterval(intervalRef.current);
  }, []); // 空依赖,仅挂载时执行一次

  return (
    <div className="App">          
       <h4>Time remaining: {timeRemaining} </h4>
    </div>
  );
}

方案3:临时关闭严格模式(不推荐)

若仅需验证问题,可在项目入口文件(如index.js)中移除<StrictMode>标签,但不建议长期使用——严格模式能帮你提前发现很多隐藏的代码问题。

内容的提问来源于stack exchange,提问作者Qin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:37:24