彻底搞懂 useEffect:依赖项、清除函数与常见陷阱

useEffect 是 React 中最常用也最容易出错的 Hook 之一。它让函数组件拥有了副作用的能力,比如发起网络请求、操作 DOM、设置定时器等。

但你是否遇到过这些问题:

  • 网络请求重复发送?
  • 清除逻辑不生效?
  • 依赖项总是报错?

今天这篇文章,我们将系统性地掌握 useEffect,并拆解它背后的机制与常见误区。


一、useEffect 到底是什么?

React 的官网定义:

The useEffect Hook lets you perform side effects in function components.

翻译一下:你可以把 useEffect 看作是类组件的 componentDidMountcomponentDidUpdatecomponentWillUnmount 的合集。

基本语法

scss 复制代码
jsx
复制编辑
useEffect(() => {
  // 副作用逻辑
  return () => {
    // 清除逻辑
  };
}, [依赖项数组]);

二、useEffect 的执行时机

React 在渲染后 执行 useEffect,具体来说:

  1. 初次渲染后执行一次(如果依赖数组为空)
  2. 当依赖项发生变化时,再次执行
  3. 在下一次执行前(或组件卸载时)调用清除函数(return 的函数)

示例:

javascript 复制代码
jsx
复制编辑
useEffect(() => {
  console.log('副作用逻辑执行');

  return () => {
    console.log('清除副作用');
  };
}, [count]);

行为如下:

  • 初次渲染:打印 副作用逻辑执行

  • count 改变时:

    • 先打印 清除副作用
    • 再打印 副作用逻辑执行

三、依赖项数组该怎么写?

这是最容易出错的地方。

✅ 正确写法:

所有在 effect 内部使用到的、在组件作用域中定义的变量和函数都应当出现在依赖数组中。

scss 复制代码
jsx
复制编辑
useEffect(() => {
  fetchData(userId); // 使用了 userId

}, [userId]); // 所以必须写上 userId

⚠️ 错误写法:

scss 复制代码
jsx
复制编辑
useEffect(() => {
  fetchData(userId);
}, []); // 虽然不报错,但 userId 改变不会触发 effect

🧠 ESLint 帮你查问题:

建议开启 eslint-plugin-react-hooks 插件,它会自动提示你遗漏的依赖项。


四、常见 useEffect 场景和写法

1. 请求接口数据

scss 复制代码
jsx
复制编辑
useEffect(() => {
  async function loadData() {
    const res = await fetch('/api/user');
    const data = await res.json();
    setUser(data);
  }
  loadData();
}, []);

✅ 依赖项为空,只执行一次,适用于「页面加载」场景。

2. 监听事件并清除

javascript 复制代码
jsx
复制编辑
useEffect(() => {
  const handleResize = () => {
    console.log(window.innerWidth);
  };

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);

3. 防抖/节流处理(useEffect + setTimeout)

scss 复制代码
jsx
复制编辑
useEffect(() => {
  const timer = setTimeout(() => {
    setDebouncedValue(value);
  }, 300);

  return () => clearTimeout(timer);
}, [value]);

五、useEffect 的常见陷阱

❌ 1. 依赖项缺失导致逻辑错误

scss 复制代码
jsx
复制编辑
useEffect(() => {
  console.log(count); // 使用了 count
}, []); // 却没写 count,容易出错

❌ 2. 无限循环

scss 复制代码
jsx
复制编辑
useEffect(() => {
  setState(1); // 改变 state,触发重新渲染
}, []); // 如果这里写了 [state] 就会死循环

❌ 3. 对象/函数作为依赖

scss 复制代码
jsx
复制编辑
useEffect(() => {
  doSomething(obj); // obj 每次引用不同
}, [obj]); // 导致 effect 总是执行

解决:用 useMemo 或 useCallback 缓存依赖项


六、useEffect vs useLayoutEffect

Hook 触发时机 使用场景
useEffect 渲染后(异步) 异步副作用、网络请求
useLayoutEffect 渲染前(同步) 读写 DOM、同步动画

如果你需要在渲染前立即测量 DOM(如宽高),可以用 useLayoutEffect


七、结语

useEffect 是 React 函数组件的副作用基石,它虽强大却也容易出错。你需要记住几点:

  • 理解它的执行时机(首次 + 依赖变化)
  • 正确维护依赖项数组
  • 使用清除函数管理副作用
  • 避免引用变化带来的误触发

学会之后,你就可以放心优雅地处理各种副作用场景了。

相关推荐
ywf12151 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭1 小时前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf7 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常10 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端