如何在 React 中正确处理 useEffect 的依赖数组以避免闭包陷阱?
如何在 React 中正确处理 useEffect 的依赖数组以避免闭包陷阱?
回答与解析:
在 React 的函数组件中,useEffect 钩子常用于执行副作用(如数据获取、订阅等)。其第二个参数是“依赖数组”,用于控制 effect 何时重新执行。如果依赖数组不完整或不正确,可能会导致“闭包陷阱”——即 effect 中捕获的是旧的变量值,而不是最新的状态或属性。
常见错误示例:
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log('Count:', count); // 总是打印初始值 0
}, 1000);
return () => clearInterval(id);
}, []); // ❌ 依赖缺失:count 没有加入依赖数组
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>;
}
在这个例子中,由于依赖数组为空,effect 只在组件挂载时运行一次,此时闭包捕获了初始的 count 值(0),后续即使 count 更新,定时器内仍引用旧值。
正确做法: 将所有在 effect 中使用的响应式值(state、props、由它们派生的值)都加入依赖数组:
useEffect(() => {
const id = setInterval(() => {
console.log('Count:', count);
}, 1000);
return () => clearInterval(id);
}, [count]); // ✅ 正确依赖
但频繁重建定时器可能不是最佳方案。更优解是使用 函数式更新 或 ref + useEffect 组合 来获取最新值而不触发不必要的重运行:
const countRef = useRef(count);
useEffect(() => {
countRef.current = count;
}, [count]);
useEffect(() => {
const id = setInterval(() => {
console.log('Count:', countRef.current); // 始终获取最新值
}, 1000);
return () => clearInterval(id);
}, []); // 仅运行一次
或者使用 useCallback 和 useReducer 等高级模式。
总结:
- 始终将 effect 中用到的所有响应式值加入依赖数组。
- ESLint 的 react-hooks/exhaustive-deps 规则能帮助检测缺失依赖。
- 若需在 effect 中访问最新状态但不想频繁触发 effect,可结合 useRef 同步状态值。

发表评论 (审核通过后显示评论):