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

为何React useEffect中设置定时器后在回调内清除无效?

问题原因

这是React 18开发环境下StrictMode的特殊行为导致的:

  • React 18的StrictMode在开发模式中会主动重复执行组件的挂载流程(挂载→模拟卸载→重新挂载),目的是帮你提前排查内存泄漏、重复订阅这类潜在问题。
  • 你的useEffect依赖为空数组,会在组件挂载时执行。在StrictMode的双重挂载流程中:
    1. 第一次挂载:创建定时器A,deployIntervalRef.current被赋值为定时器A的ID。
    2. React模拟组件卸载,但你没有给useEffect添加清理函数,所以定时器A并没有被清除,仍在后台运行。
    3. 第二次挂载:创建定时器B,deployIntervalRef.current被覆盖为定时器B的ID。
  • 当定时器A的回调触发时,deployIntervalRef.current已经是定时器B的ID了——你执行clearInterval(deployIntervalRef.current)实际上只清除了定时器B,但定时器A本身还在运行,会继续周期性触发回调,导致控制台多次输出。
解决方案

给useEffect添加清理函数,在组件卸载时清除当前的定时器,这样StrictMode模拟卸载时就能清理掉第一次创建的定时器:

function App() {
  const deployIntervalRef = React.useRef(null);
  
  React.useEffect(() => {
    deployIntervalRef.current = setInterval(() => {
      console.log("interval handler");
      console.log(deployIntervalRef.current);
      clearInterval(deployIntervalRef.current);
      deployIntervalRef.current = null;
    }, 2000);

    // 新增清理函数,组件卸载时清除定时器
    return () => {
      if (deployIntervalRef.current) {
        clearInterval(deployIntervalRef.current);
        deployIntervalRef.current = null;
      }
    };
  }, []);
  
  return <div></div>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App/></React.StrictMode>);

另外,如果你只是需要延迟执行一次操作,用setTimeout会更简洁,不需要额外清除setInterval:

function App() {
  React.useEffect(() => {
    const timer = setTimeout(() => {
      console.log("timeout handler");
    }, 2000);

    return () => clearTimeout(timer);
  }, []);
  
  return <div></div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 03:52:36