React Hooks闭包陷阱(Stale Closure)详解
什么是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。