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

如何在useEffect中优雅使用最新的状态变量?

如何在useEffect中优雅使用最新的状态变量?

嘿,这个场景我太熟了!你碰到的就是React里经典的闭包陷阱——useEffect里的定时器函数捕获的是组件首次渲染时的count值(也就是1),所以10秒后打印的不是预期的10,而是初始的1。而且你说的没错,如果直接把count加到useEffect的依赖里,会导致每次count更新都重新创建定时器,那计数速度会直接起飞,完全是反效果。

给你分享两个优雅的解决思路,都能避开这些坑:

方法一:用useRef保存最新状态的引用

React的useRef就是专门用来解决这种“需要跨渲染周期访问最新值”的场景的,它的current属性是可变的,不会像状态变量那样被闭包“固定”住。我们只需要加一小段代码同步状态到ref就行:

function Counter() {
  const [count, setCount] = useState(1);
  const countRef = useRef(count);

  // 每次count更新时,同步到ref里
  useEffect(() => {
    countRef.current = count;
  }, [count]);

  useEffect(() => {
    const timeout = setInterval(() => setCount(prev => prev + 1), 1000);

    setTimeout(() => console.log(`Count after 10': ${countRef.current}`), 10000);

    return () => clearTimeout(timeout);
  }, []);

  return <span>{count}</span>;
}

原理很简单:单独的那个useEffect会在每次count变化时自动把最新值同步到countRef.current里,这样不管什么时候访问这个ref,拿到的都是当前最新的状态。而且外层的useEffect依然保持空依赖,不会重复创建定时器,完美解决了你的问题。

方法二:在状态更新时同步更新ref(可选)

如果你不想多写一个useEffect,也可以在更新状态的同时手动同步ref,不过这种方式适合状态更新逻辑比较集中的场景:

function Counter() {
  const [count, setCount] = useState(1);
  const countRef = useRef(count);

  const increment = () => {
    const newCount = count + 1;
    countRef.current = newCount;
    setCount(newCount);
  };

  useEffect(() => {
    const timeout = setInterval(increment, 1000);

    setTimeout(() => console.log(`Count after 10': ${countRef.current}`), 10000);

    return () => clearTimeout(timeout);
  }, []);

  return <span>{count}</span>;
}

不过要注意,如果后续有其他地方也会更新count,别忘了同步更新ref,不然就会出现ref和状态不一致的情况,所以方法一的通用性会更强一些。

你问能不能不用额外对象包裹就传引用?其实useRef就是React官方给的最优解了,它没有多余的样板代码,完全符合React的设计逻辑,比你自己手动封装对象要靠谱得多。

备注:内容来源于stack exchange,提问作者Herbert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 12:08:06