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

为何setTimeout中的setState会触发两次控制台日志?

React状态更新控制台重复打印问题解析

问题描述

我在React组件中定义了number和string两个state变量,在func函数中更新它们。已知组件每次渲染都拥有独立的setTimeout,当我快速在输入框中输入文本时,预期number状态最终仅会变为1一次,尽管结果确实是1,但控制台却打印了两次,这是为什么?

export default function Mycomponent() {
  const [number, setnumber] = useState(0);
  const [string, setstring] = useState('');
  
  function func(event) {
    setstring(event.target.value);
    setTimeout(() => {
      setnumber(number + 1);
    }, 3000);
    // 每次渲染都有独立的setTimeout,所以我快速输入文本时,预期number最终只会变成1一次
    // 结果确实是1,但控制台打印了两次,这是为什么?
  }
  
  console.log(number, string);
  
  return (
    <>
      <input type='text' onChange={(e) => { func(e) }} value={string} />
    </>
  )
}

控制台日志截图

原因解析

1. 闭包捕获旧状态值

每次输入触发onChange时,func会被调用,同时setstring更新输入内容,触发组件重新渲染。每次渲染生成的func都是独立的,里面的setTimeout回调会捕获当前渲染周期的number值:

  • 第一次输入时,组件渲染时number为0,对应的setTimeout回调捕获到的number就是0;
  • 快速第二次输入时,组件再次渲染,此时第一个setTimeout还未执行,number依然是0,新的func里的setTimeout回调捕获的number还是0。

3秒后两个回调依次执行,都会调用setnumber(0 + 1),也就是尝试把number设置为1。

2. 开发环境严格模式的渲染机制

第一次setnumber(1)执行时,number从0变为1,触发组件渲染,控制台打印出1和当前字符串;
第二次setnumber(1)执行时,虽然新值和当前number的状态(1)相同,但React在开发环境的严格模式下,会故意执行两次状态更新和渲染操作,用于检测组件中是否存在不安全的副作用。这就是你看到控制台打印两次1的原因。如果是生产环境,第二次相同值的状态更新会被React跳过,不会触发渲染,控制台只会打印一次1。

解决方法

如果希望多次setTimeout回调能基于最新的状态更新,应该使用函数式更新,让React自动传入当前最新的状态值:

setTimeout(() => {
  setnumber(prevNumber => prevNumber + 1);
}, 3000);

这样不管有多少个延迟执行的回调,都会基于当前最新的number值来计算新状态,最终number会等于你输入的次数,而不是每次都被设置为1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 20:47:33