Skip to content

useEffect:副作用、依赖数组与清理函数

知识背景

纯渲染(根据 state/props 算出 UI)之外的操作——请求接口、订阅事件、改 document.title、定时器——在 React 里称为副作用,通常放在 useEffect 里,避免阻塞渲染本身。
Class 时代对应 componentDidMount / componentDidUpdate / componentWillUnmountuseEffect 把「挂载后 / 依赖变了 / 卸载前」统一成一套 API,但依赖写错是线上 bug 第一大来源。


知识详解与通俗解释

1. 三种常见写法

jsx
// 挂载 + 每次更新后都会跑(慎用,易死循环或多余请求)
useEffect(() => { ... });

// 仅挂载时跑一遍(第二个参数为空数组)
useEffect(() => { ... }, []);

// 当 a 或 b 变化时跑
useEffect(() => { ... }, [a, b]);

通俗说:React 在「提交 DOM 之后」异步执行 effect;依赖数组告诉 React 哪些外部值变了才需要重新执行这份副作用

2. 清理函数:return 一个函数

jsx
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []);

卸载组件或依赖变化导致重新执行 effect 之前,会先跑上一次 effect 返回的清理函数。适合:取消订阅、清定时器、abort fetch

3. 依赖原则(eslint-plugin-react-hooks)

  • effect 体内用到的、来自组件作用域的响应式值(props、state、以及若变化应重跑的函数)一般都要进依赖数组。
  • useCallback / useMemo 稳定引用,是为了正确收窄依赖,而不是为了骗过 linter。
  • 若故意省略依赖:要么抽逻辑到子组件,要么用 ref 保存「仅读最新、不参与渲染」的值,并清楚自己在做什么。

4. useEffect vs useLayoutEffect(预告)

两者触发时机不同:useEffect 在浏览器绘制之后useLayoutEffect 在 DOM 更新后、绘制之前同步执行。测量布局、避免闪烁时用后者;默认用前者减少阻塞。


总结

  • useEffect 管副作用;依赖数组 = 这份副作用与谁同步
  • 清理函数防止内存泄漏与重复订阅。
  • 遵守 exhaustive-deps 能避免大量「请求没重发、订阅的是旧 id」类 bug。