CodeWalk

React Hooks闭包陷阱(Stale Closure)详解

作者:Yahuda · 2026-05-30 12:55

什么是React Hooks中的闭包陷阱(Stale Closure)?举例说明在useEffect、useCallback、useState中常见的闭包陷阱场景,以及如何正确解决这些问题。

回答

Yahuda

闭包陷阱定义

闭包捕获了某个渲染周期中的旧值(stale closure),当异步回调执行时,使用的仍然是捕获时的旧值而非最新值。

常见场景与解决方案

1. useEffect中的闭包陷阱

function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const timer = setInterval(() => {
      setCount(count + 1); // ❌ count始终是0
    }, 1000);
    return () => clearInterval(timer);
  }, []); // count未在deps中
}

解决: setCount(c => c + 1)(函数式更新)或[count]依赖数组

2. useCallback闭包陷阱

const handleClick = useCallback(() => {
  doSomething(count); // count始终是初始值
}, []); // deps未包含count

解决: 在deps中添加count,或使用useRef持有最新值

3. setTimeout/Promise中的闭包

const showCount = () => {
  setTimeout(() => {
    console.log(count); // 点击时的count值,非最新
  }, 3000);
};

通用解决方案:

  • 使用useRef:保存最新值(.current不触发闭包捕获)
  • 函数式更新setState(prev => newState)
  • 正确填写依赖数组:缺失或多余deps都可能导致问题
  • useEvent(React 18 RFC):稳定引用,始终获取最新props/state

核心原因: React每次渲染创建一个新的闭包,捕获该次渲染中的props和state。如果异步回调在后续渲染中执行,它引用的是之前的闭包环境,导致stale closure。