前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >React 播客专栏 Vol.14|useEffect 背后都在忙些什么?

React 播客专栏 Vol.14|useEffect 背后都在忙些什么?

作者头像
前端达人
发布于 2025-05-21 06:37:50
发布于 2025-05-21 06:37:50
9100
代码可运行
举报
文章被收录于专栏:前端达人前端达人
运行总次数:0
代码可运行

今天我们聚焦一个让人又爱又怕的 Hook —— useEffect,它是副作用的管理器,是函数组件的“隐藏执行者”。

我们将从基础语法,到执行机制,再到真实开发场景,完整拆解 useEffect 的使用逻辑与工作原理。

一、你为什么用不好 useEffect

很多人第一次用 useEffect,就遇到这些问题:

🌀 “为什么副作用执行了两次?” 😱 “组件卸载时怎么还在请求接口?” 💣 “useEffect 写在按钮里怎么报错了?”

这些其实都跟它的规则和生命周期时机有关。

我们来带着这些问题,逐个拆解。

二、什么是副作用?(Side Effects)

📌 通俗解释

副作用 = 不直接参与 UI 渲染、但“影响了组件生命周期”的逻辑行为

📦 常见副作用包括:

  • 接口请求(fetch, axios)
  • 添加 DOM 事件监听
  • 设置定时器、interval
  • 第三方库的注册 / 订阅
  • 页面标题修改、手动操作 DOM

👀 可以类比成做饭时顺手擦桌子 —— 主任务是做饭,擦桌子是副任务,但它依然需要安排好时机执行。

三、基本语法与用法

useEffect 的标准写法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
useEffect(() => {
  // 副作用代码
}, [依赖项]);

解释:

  • 第一参数:副作用函数(Effect Function)
  • 第二参数:依赖项数组(Dependencies Array)

📌 不同依赖写法 = 不同执行时机:

写法

执行时机说明

无依赖项

每次渲染后都会执行

空数组 []

仅在挂载时执行一次(相当于 componentDidMount)

有依赖项 [search]

仅当指定依赖项发生变化时才重新执行副作用

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 每次渲染后都执行
useEffect(() => {
  console.log("组件更新了");
});

// 指定依赖项
useEffect(() => {
  console.log("search 变化了");
}, [search]);

// 只在挂载时执行一次
useEffect(() => {
  console.log("组件加载了");
}, []);

✅ 推荐每次写 useEffect 都明确依赖数组,避免无意识触发。

四、注意!useEffect 的两条硬性规则

🧱 Hooks 规则必须记牢

  1. 只能在函数组件最顶层调用❌ 不能放在条件语句、循环、嵌套函数中
  2. 只能在函数组件或自定义 Hook 中调用❌ 不可在普通函数或 class 组件中使用

错误示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function BadComponent() {
  function handleClick() {
    // ❌ 错误:在事件处理函数中调用 Hook
    useEffect(() => {
      console.log("点击副作用");
    });
  }

  return <button onClick={handleClick}>Click</button>;
}

五、useEffect 的执行机制图解 🧠

生命周期时机

useEffect 行为

初次渲染完成

执行副作用函数

依赖项变化(非空)

清除上一次副作用 → 执行新的副作用函数

组件卸载

清除副作用

📌 精髓:

  • 有依赖项 → 比较依赖变化 → 触发副作用
  • 副作用函数返回值是“清理函数” → 下一次执行前/卸载时运行

六、清理函数:打扫战场的副将

清理函数是 useEffect 返回的函数,它会在副作用重跑前或组件卸载时被调用

常见用途:

  • 清除事件监听器
  • 取消订阅
  • 清除定时器
  • 终止请求

代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
useEffect(() => {
  const handler = () => console.log("点击");
  document.addEventListener("click", handler);

  return () => {
    document.removeEventListener("click", handler); // 清理
  };
}, []);

🧼 类比生活:副作用是吃完饭,清理函数就是洗碗 ✨

七、实战:数据获取场景中的最佳实践

👨‍💻 获取后端数据,是 useEffect 最典型的用法之一。

🎯 场景:组件加载时请求数据并展示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function PostsPage() {
  const [posts, setPosts] = useState<PostData[]>([]);

  useEffect(() => {
    getPosts().then((data) => {
      setPosts(data);
    });
  }, []);
}

📌 Tips:

  • 如果使用 async/await,记得 不能直接在 useEffect 外层用 async!
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
useEffect(() => {
  async function fetchData() {
    const res = await fetch('/api/posts');
    const data = await res.json();
    setPosts(data);
  }

  fetchData(); // 内部调用 async 函数
}, []);

八、进阶优化:处理加载状态 & 错误处理

✅ 推荐增加 isLoadingerror 状态来更好管理异步流程:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export function PostsPage() {
  const [isLoading, setIsLoading] = useState(true);
  const [posts, setPosts] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    let ignore = false;

    async function fetchData() {
      try {
        const res = await fetch('/api/posts');
        const data = await res.json();
        if (!ignore) setPosts(data);
      } catch (err) {
        if (!ignore) setError(err);
      } finally {
        if (!ignore) setIsLoading(false);
      }
    }

    fetchData();

    // 清理副作用
    return () => {
      ignore = true;
    };
  }, []);
}

📌 说明:

  • 使用 ignore flag 防止组件卸载后仍更新状态
  • 真实项目中也可使用 AbortController 中断请求

九、React 严格模式下副作用执行两次?

⚠️ 在开发模式中,React 18 严格模式(StrictMode)下会故意模拟卸载 + 重建

  • 你会看到副作用执行两次,这是“检测副作用是否安全”的设计
  • 生产环境中只会执行一次,不用担心

🔟 总结回顾 🧠

✅ 关键词

📌 要点说明

副作用(Side Effect)

组件渲染之外的逻辑:获取数据、DOM 操作、订阅等

useEffect()

处理副作用的 Hook,配合依赖项控制执行时机

清理函数

返回值函数:在下一次执行/卸载前清除副作用

异步请求最佳实践

使用 async/await + 错误处理 + loading 状态管理

Hook 使用规则

只能顶层调用,不能用于 class 组件、if/for 结构中

👋 喜欢这一期的朋友别忘了:

点赞、转发、收藏!有任何问题也欢迎在评论区留言,我会逐条解答!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验