如何在 React 中正确处理 useEffect 的依赖数组以避免闭包陷阱?
如何在 React 中正确处理 useEffect 的依赖数组以避免闭包陷阱?
回答与解析:
在 React 中,useEffect 的闭包陷阱通常发生在依赖数组(dependency array)未正确包含 effect 中使用的状态或 props 变量时,导致 effect 内部捕获的是“过期”的变量值。
问题示例:
function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // 每次都基于初始 count(0)加 1
    }, 1000);
    return () => clearInterval(id);
  }, []); // ❌ 依赖数组为空,count 被闭包捕获为初始值
  return <div>{count}</div>;
}
上述代码中,由于依赖数组为空,setInterval 中的 count 始终是初始值 0,导致 count 只会从 0 变成 1,之后不再递增。
正确做法:
- 将 count 加入依赖数组(但会频繁重建定时器):
useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, [count]); // ✅ 但每次 count 变化都会清除并重建定时器
- 更优方案:使用函数式更新(推荐)
useEffect(() => {
  const id = setInterval(() => {
    setCount(prev => prev + 1); // ✅ 始终基于最新状态更新
  }, 1000);
  return () => clearInterval(id);
}, []); // 依赖数组可为空,因为不依赖外部变量
- 高级场景:使用 useRef 同步最新值(如需在非状态更新逻辑中使用最新值)
const countRef = useRef(count);
useEffect(() => {
  countRef.current = count; // 同步最新值
}, [count]);
useEffect(() => {
  const id = setInterval(() => {
    console.log('Current count:', countRef.current); // 获取最新值
  }, 1000);
  return () => clearInterval(id);
}, []);
总结:
- 若 effect 中使用了状态或 props,必须将其加入依赖数组。
- 对于状态更新,优先使用函数式更新(setCount(prev => ...))避免依赖问题。
- 对于需要访问最新值但又不想频繁触发 effect 的场景,可结合 useRef 同步值。
- 可借助 ESLint 插件 react-hooks/exhaustive-deps 自动检查依赖完整性。

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