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

React倒计时定时器打印值始终为10无法触发清零逻辑如何解决?

问题根因

  • 闭包陷阱:counter函数在组件首次渲染时创建,捕获了初始状态time=10,后续组件重渲染生成的新counter函数不会被已经启动的定时器调用,所以每次执行定时器回调拿到的永远是初始的10,永远进不了else分支
  • 定时器ID未正确存储:原代码中没有存储setInterval的返回值,clearInterval调用时的timer变量属于未定义,就算满足终止条件也清不掉定时器
  • 普通变量flag无法跨渲染周期持久化:每次组件重渲染都会重置flag = true,没有起到防止重复启动定时器的作用

修复方案

修改后的完整JS代码如下:

const {useState, useRef, useEffect} = React;

const Timer = () => {
  const [time, setTime] = useState(10);
  // 用useRef存储定时器ID,跨渲染周期保持引用
  const timerRef = useRef(null);
  // 标记定时器是否已经启动,防止重复点击开启多个定时器
  const isStartedRef = useRef(false);

  const counter = () => {
    setTime(prevTime => {
      console.log(prevTime);
      if (prevTime <= 1) {
        // 倒计时到0时清除定时器
        clearInterval(timerRef.current);
        isStartedRef.current = false;
        console.log('out');
        return 0;
      }
      return prevTime - 1;
    })
  }

  const handleClick = () => {
    if (!isStartedRef.current) {
      isStartedRef.current = true;
      timerRef.current = setInterval(counter, 500);
    }
  }

  // 组件卸载时清除定时器,避免内存泄漏
  useEffect(() => {
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    }
  }, [])

  return(
    <div>
      <div>{time}</div>
      <button className="start" onClick={handleClick}>START</button>
    </div>
    )
}

ReactDOM.render(
  <Timer />,
  document.getElementById("react")
);

HTML代码无需修改。

修改点说明

  • 引入useRef存储定时器ID和启动状态,确保值不会因为组件重渲染被重置
  • 将倒计时判断逻辑放到setTime的更新回调中,直接取上一次的最新状态值,避开闭包陷阱
  • 增加组件卸载时清理定时器的逻辑,避免组件销毁后定时器还在运行导致内存泄漏
  • 修复了未定义timer变量的问题,所有定时器操作都通过timerRef.current调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 10:15:01