彻底搞懂 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 函数组件的副作用基石,它虽强大却也容易出错。你需要记住几点:

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

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

相关推荐
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax