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

React Hooks中setInterval内调用setCount触发两次渲染的原因

问题原因

这个现象是React 18+ 版本开发环境下,严格模式(StrictMode)的副作用校验机制触发的,属于框架的预期行为,不是代码逻辑bug。

具体执行逻辑拆解

React 18 对严格模式做了升级,开发环境下每个组件首次挂载时,会主动执行一轮「挂载 → 模拟卸载 → 重新挂载」的流程,目的是提前暴露副作用未正确清理的隐蔽问题,这个重复执行逻辑仅在开发环境生效,生产环境打包后不会出现。
对应你贴的代码,完整执行流程如下:

  • 组件首次挂载阶段
    1. 组件函数执行,打印useEffect before 60、useEffect after 60
    2. 执行空依赖的useEffect回调,打印useEffect 60,注册第一个setInterval定时器,这个定时器的闭包捕获的count是初始值60
    3. React触发模拟卸载,执行useEffect返回的清理函数——但你写的清理函数是空逻辑,没有调用clearInterval清除第一个定时器,导致这个定时器成了游离的残留副作用
  • 组件模拟卸载后立即重挂载阶段
    1. 组件函数再次执行,此时count还没更新,依然打印useEffect before 60、useEffect after 60
    2. 再次执行空依赖的useEffect回调,打印useEffect 60,注册第二个setInterval定时器,闭包同样捕获初始值60
  • 1秒后定时器触发阶段
    1. 之前残留的第一个定时器触发,打印count 60,执行setCount(59)
    2. 新注册的第二个定时器触发,同样打印count 60,执行setCount(59)
    3. 因为两次setCount传入的都是固定值59,React会对相同的state做跳过重渲染的优化,不会触发两次组件重渲染,但两个定时器回调确实都执行了,就是你看到日志重复的原因。

修复方案

不要通过移除严格模式规避这个现象,严格模式能帮你提前排查很多潜在bug,正确做法是补全副作用的清理逻辑:

useEffect(() => {
    console.log("useEffect", count);
    const timeId = setInterval(() => {
        console.log("count", count);
        setCount(59);
    }, 1000);
    // 组件卸载/effect重执行前清除上一个定时器
    return () => {
        clearInterval(timeId);
    };
}, []);

额外提一句:你现在的写法里,定时器闭包永远捕获的是首次渲染的count=60,如果后续要做倒计时等需要拿到最新state的逻辑,要么把对应state加入useEffect依赖数组,要么使用函数式更新写法setCount(prev => prev - 1),避免闭包陈旧值的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:15:43