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

React中useState状态更新后值始终不变的原因咨询

为什么React状态在定时器中始终不更新?

我定义了如下状态:

const [number, SetNumber] = React.useState<number>(0)

随后在首次渲染的钩子中创建了更新函数和定时器:

const increaseNumber = () => {
  console.log(number);
  SetNumber(number + 1);
};

React.useEffect(() => {
  setInterval(() => {
    increaseNumber();
  }, 1000);
}, []);

实际运行后,console.log(number)的输出始终是:

0
0
0
0
0

但我期望的输出是:

0
1
2
3
4

完整代码如下:

import * as React from 'react';

const App = () => {
  const [number, SetNumber] = React.useState<number>(0);

  const increaseNumber = () => {
    console.log(number);
    SetNumber(number + 1);
  };

  React.useEffect(() => {
    setInterval(() => {
      increaseNumber();
    }, 1000);
  }, []);

  return (
    <div>
      <h1>{number}</h1>
    </div>
  );
};

export default App;

问题原因

这是React闭包陷阱导致的:

  • 你的useEffect依赖数组是空的,意味着它只会在组件首次渲染时执行一次,创建定时器。
  • 首次渲染时,increaseNumber函数通过闭包捕获了当时的number值(也就是初始值0)。
  • 之后组件每次因为状态更新重新渲染时,都会创建一个新的increaseNumber函数,但定时器里调用的始终是首次渲染时创建的那个旧函数——它里面的number永远是0。
  • 所以每次调用SetNumber(number + 1)其实都是SetNumber(0 + 1),状态被反复设置为1,但console.log输出的还是闭包里保存的0。

解决方案

有两种常用的修复方式:

方式1:使用函数式更新

直接给SetNumber传递一个函数,它会接收当前最新的状态值,这样就不需要依赖外部的number变量:

const increaseNumber = () => {
  // 用函数式更新获取最新状态
  SetNumber(prevNumber => {
    console.log(prevNumber);
    return prevNumber + 1;
  });
};

React.useEffect(() => {
  setInterval(() => {
    increaseNumber();
  }, 1000);
}, []);

方式2:用useCallback包裹函数并更新依赖

通过useCallback让increaseNumber依赖number,同时把它加入useEffect的依赖数组,每次number更新时重新创建定时器(记得清理旧定时器):

import * as React from 'react';

const App = () => {
  const [number, SetNumber] = React.useState<number>(0);

  // 用useCallback包裹,依赖number
  const increaseNumber = React.useCallback(() => {
    console.log(number);
    SetNumber(number + 1);
  }, [number]);

  React.useEffect(() => {
    const timer = setInterval(() => {
      increaseNumber();
    }, 1000);
    // 清理旧定时器
    return () => clearInterval(timer);
  }, [increaseNumber]);

  return (
    <div>
      <h1>{number}</h1>
    </div>
  );
};

export default App;

两种方式都能让你得到期望的递增输出,推荐第一种方式,更简洁高效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:01:16