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

React使用useEffect实现倒计时时setInterval未被正常清除问题

问题根本原因
  • 你编写的useEffect确实正确将timeout加入了依赖数组,每次timeout更新时,会先执行上一轮返回的清理函数清除旧定时器,再创建新的定时器。
  • 当timeout被更新为0的这一轮组件渲染完成后,useEffect会正常执行并创建一个新的定时器,这个定时器的回调函数通过闭包捕获的timeout值固定为当前的0。
  • 因为回调中只有timeout>0时才会调用状态更新函数修改timeout,当timeout为0时不会触发状态更新,timeout的值会永久停留在0,useEffect的依赖没有发生变化,就不会再触发执行,也就不会清理这个最后创建的定时器,因此它会一直每秒执行一次,打印日志。

额外提示:你把状态名命名为timeout,状态更新函数命名为setTimeout,和全局的window.setTimeout重名,容易出现变量指向错误,建议调整命名避免混淆。

修复方案

方案1:创建定时器前判断倒计时是否结束,为0则不创建定时器

function CounterGame() {
  const [timeout, setTimeout] = React.useState(3);

  React.useEffect(() => {
    // 倒计时结束直接返回,不创建定时器
    if (timeout <= 0) return;
    let id = window.setInterval(() => {
      setTimeout(x => x - 1 );
      console.log("in setInterval", timeout);
    }, 1000)

    return () => {
      window.clearInterval(id);
    }
  }, [timeout])
  return (
    <div className="App">
      See instructions.
      <h1>{timeout} seconds</h1>
      {!timeout && <button>Click</button>}
    </div>
  );
}

方案2:定时器回调中主动判断结束条件,满足则清除当前定时器

function CounterGame() {
  const [timeout, setTimeout] = React.useState(3);

  React.useEffect(() => {
    let id = window.setInterval(() => {
      if (timeout > 0) {
        setTimeout(x => x - 1 );
      } else {
        // 倒计时结束主动清理当前定时器
        window.clearInterval(id);
      }
      console.log("in setInterval", timeout);
    }, 1000)

    return () => {
      window.clearInterval(id);
    }
  }, [timeout])
  return (
    <div className="App">
      See instructions.
      <h1>{timeout} seconds</h1>
      {!timeout && <button>Click</button>}
    </div>
  );
}

方案3:改用setTimeout实现,更适配倒计时递减场景

function CounterGame() {
  const [timeout, setTimeout] = React.useState(3);

  React.useEffect(() => {
    if (timeout <= 0) return;
    let id = window.setTimeout(() => {
      setTimeout(x => x - 1 );
    }, 1000)

    return () => {
      window.clearTimeout(id);
    }
  }, [timeout])
  return (
    <div className="App">
      See instructions.
      <h1>{timeout} seconds</h1>
      {!timeout && <button>Click</button>}
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 03:15:00