直接回答:每次渲染生成新的函数闭包,捕获当次渲染的 state/props 快照;若闭包被延迟执行(setTimeout、订阅回调、防抖函数),拿到的仍是创建时的旧快照——这就是闭包陈旧。本质不是 React 的 bug,而是“函数式渲染快照模型”与 JS 闭包语义的必然产物。
展开解析:典型翻车现场:useEffect 空依赖里注册事件回调用到 state,state 变了回调里还是初值;setInterval 里 count + 1 永远从 0 开始;异步请求完成回调读到过期数据覆盖新状态。系统性解法按场景选:函数式更新——setState(c => c + 1) 不依赖闭包里的值,只依赖 React 注入的最新值,简单累加场景首选;useRef 逃逸——把最新值同步进 ref(current 在渲染外可变),回调里读 ref.current,代价是脱离响应式追踪;依赖数组诚实——把用到的值列进 deps 让回调随渲染重建,ESLint exhaustive-deps 就是干这个的,但重建意味着重新订阅/重新计时,要评估代价;useEffectEvent(React 19 实验性转正方向)——专门声明“读最新值但不触发重建”的事件函数,语义上分离“响应式逻辑”与“事件逻辑”。进阶认知:Class 组件的 this 永远指向最新实例反而没这个问题,Hooks 用快照换掉了 this 的隐式可变——理解“渲染即快照”的心智模型比背解法重要,所有 Hooks 怪问题(effect 时序、memo 失效)都能从它推导。调试技巧:怀疑陈旧时在回调里打印 ref.current 与 state 对比,立判。
追问方向:useRef 同步最新值为什么是 anti-pattern 的边界案例?并发模式下陈旧闭包为何更常见?
(约 490 字)