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

React中useEffect内setState更新后日志值始终为0的问题排查

问题原因与解决方案

这是React开发里非常常见的闭包陷阱问题,我来给你一步步拆解清楚:

为什么打印的currentSalary始终是0?

当你在useEffect中创建setInterval定时器时,回调函数会捕获它创建时所在作用域的currentSalary变量——也就是组件第一次渲染时的初始值0。

每次调用setCurrentSalary更新状态时,组件会重新渲染,生成一个全新的currentSalary变量(持有更新后的值),但定时器的回调函数仍然绑定在最初那个闭包上,引用的还是初始的0,所以你在定时器里打印的永远是初始值,哪怕页面显示已经正常更新了。

另外你的代码还有个隐藏问题:useEffect没有设置依赖数组,这会导致每次组件渲染都会创建一个新的定时器,最终多个定时器同时运行,虽然页面看起来正常,但会造成不必要的性能浪费。

修复方案

方案1:用useRef保存最新状态值

useRef的.current属性是可变的,而且不会触发组件重新渲染,我们可以用它同步保存最新的currentSalary值,这样定时器里就能拿到实时更新的状态:

import { useState, useEffect, useRef } from 'react';

function App() { 
  const [date, setDate] = useState(); 
  const [salaryPerHour] = useState(145); 
  const [salaryPerSecond] = useState(salaryPerHour / 60 / 60); 
  const initialSalary = 0; 
  const [currentSalary, setCurrentSalary] = useState(initialSalary);
  
  // 创建ref保存最新的currentSalary
  const currentSalaryRef = useRef(currentSalary);

  // 当currentSalary更新时,同步更新ref的值
  useEffect(() => {
    currentSalaryRef.current = currentSalary;
  }, [currentSalary]);

  useEffect(() => { 
    console.log("Use effect at App.js"); 
    if (!date) { 
      const intervalId = setInterval(() => { 
        updateDate(); // 确保你已经实现了这个更新日期的函数
        setCurrentSalary(prevCurrentSalary => prevCurrentSalary + salaryPerSecond); 
        // 打印ref中保存的最新值
        console.log(currentSalaryRef.current); 
      }, 1000); 

      // 清理函数:组件卸载或依赖变化时清除定时器
      return () => clearInterval(intervalId);
    } 
  }, [date, salaryPerSecond]); // 添加依赖数组,避免重复创建定时器

  // 示例updateDate函数实现
  const updateDate = () => {
    setDate(new Date());
  };

  return (
    <div>
      <p>当前工资:{currentSalary.toFixed(4)}</p>
    </div>
  );
}

方案2:单独用useEffect监听状态变化打印

如果你只是想在currentSalary更新时打印它的值,完全不需要在定时器里做,直接用另一个useEffect监听状态变化即可,代码更简洁:

// 监听currentSalary变化,每次更新时自动打印最新值
useEffect(() => {
  console.log(currentSalary);
}, [currentSalary]);

额外提醒

  • 永远不要忽略useEffect的依赖数组,否则会导致不必要的重复执行,甚至内存泄漏。
  • 当你需要在异步操作(比如定时器、网络请求)中访问最新的状态时,优先考虑使用函数式更新(比如setCurrentSalary(prev => prev + ...))或者useRef,这两种方式都能避开闭包陷阱。

内容的提问来源于stack exchange,提问作者Bobby Rösehag

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 22:02:55