useEffect最详细的用法

什么是 useEffect?

在 React 中,组件渲染后可能会需要执行一些操作,比如:

  • 数据获取 (Data Fetching)
  • 设置订阅或计时器
  • 手动操作 DOM
  • 添加事件监听器

这些操作在 React 中被称为"副作用"(side effects)。useEffect 钩子让你可以在函数组件中执行这些副作用。它在组件渲染后运行,并且可以让你控制它何时重新运行。

useEffect 的基本结构

useEffect 接受两个参数:

  1. 一个副作用函数(effect function):这是包含副作用逻辑的函数。
  2. 一个可选的依赖项数组(dependency array):它决定了副作用函数何时重新运行。
javascript 复制代码
useEffect(() => {
  // 你的副作用代码
  // 例如:数据请求、设置事件监听器等
  return () => {
    // 可选的清理函数
    // 例如:取消订阅、移除监听器等
  };
}, [/* 依赖项数组 */]);

依赖项数组的三种情况

依赖项数组是控制 useEffect 行为的关键。

1. 没有依赖项数组

如果省略第二个参数,useEffect 将在每次组件渲染后都运行。

用例: 这是一个罕见 的用例。它会在任何状态或 props 改变时都执行副作用。这通常会导致性能问题或无限循环。

javascript 复制代码
useEffect(() => {
  console.log('每次渲染后都运行');
});

警告: 这通常不是你想要的效果,因为它会造成不必要的性能开销。

2. 空依赖项数组 []

如果传入一个空数组 []useEffect 将只在组件首次挂载时运行一次。

用例: 这是用于执行一次性设置或数据获取的常见模式。它与类组件中的 componentDidMount 生命周期方法类似。

ini 复制代码
useEffect(() => {
  console.log('组件已挂载');

  // 例如:在此处进行初始数据获取
  const fetchData = async () => {
    const response = await fetch('https://api.example.com/data');
    const result = await response.json();
    // ...
  };
  fetchData();
}, []); // 依赖项数组为空

3. 带有值的依赖项数组 [dep1, dep2]

如果依赖项数组包含一个或多个值,useEffect 将在组件首次挂载时运行,并且在依赖项中的任何一个值发生变化时再次运行。

用例: 当你需要根据某些 propsstate 的变化来执行副作用时,这是最常用的模式。

javascript 复制代码
function UserProfile({ userId }) {
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    console.log(`用户ID ${userId} 已改变,正在获取新数据`);
    const fetchUserData = async () => {
      const response = await fetch(`https://api.example.com/users/${userId}`);
      const result = await response.json();
      setUserData(result);
    };
    fetchUserData();
  }, [userId]); // 当 userId 改变时,重新运行这个 effect

  return (
    <div>
      {userData ? (
        <pre>{JSON.stringify(userData, null, 2)}</pre>
      ) : (
        <p>正在加载用户信息...</p>
      )}
    </div>
  );
}

清理函数 (Cleanup Function)

有些副作用(如事件监听器、计时器或订阅)需要被清理,以防止内存泄漏。useEffect 的副作用函数可以返回一个清理函数

React 会在组件卸载时,以及在下一次运行副作用函数之前,执行这个清理函数。

用例: 移除事件监听器或取消订阅。

javascript 复制代码
useEffect(() => {
  const handleScroll = () => {
    console.log('正在滚动...');
  };
  window.addEventListener('scroll', handleScroll);

  // 返回清理函数
  return () => {
    console.log('正在移除滚动监听器...');
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

最佳实践和注意事项

  • 遵循依赖项规则: 始终将 effect 函数中使用的所有 propsstate 和函数都包含在依赖项数组中。ESLint 的 react-hooks/exhaustive-deps 规则会对此进行检查,并给出警告。
  • 使用 useCallbackuseMemo 如果你将一个函数或对象作为 useEffect 的依赖项,并且它在每次渲染时都会被重新创建,那么 useEffect 也会重复运行。在这种情况下,请使用 useCallback 来缓存函数,或使用 useMemo 来缓存对象,以确保依赖项的引用是稳定的。
  • 拆分 useEffect 钩子: 如果你的组件有多个不相关的副作用,最好为每个副作用使用独立的 useEffect 钩子。这能提高代码的可读性,并使依赖项管理更加清晰。
相关推荐
一 乐几秒前
绿色农产品销售|基于springboot + vue绿色农产品销售系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端·宠物
zzjyr1 分钟前
Webpack 生命周期原理深度解析
前端
xiaohe06014 分钟前
💘 霸道女总裁爱上前端开发的我
前端·游戏开发·trae
sophie旭7 分钟前
内存泄露排查之我的微感受
前端·javascript·性能优化
k***19514 分钟前
Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲
前端·数据库·spring
rgeshfgreh1 小时前
Spring事务传播机制深度解析
java·前端·数据库
Hilaku2 小时前
我用 Gemini 3 Pro 手搓了一个并发邮件群发神器(附源码)
前端·javascript·github
IT_陈寒2 小时前
Java性能调优实战:5个被低估却提升30%效率的JVM参数
前端·人工智能·后端
快手技术2 小时前
AAAI 2026|全面发力!快手斩获 3 篇 Oral,12 篇论文入选!
前端·后端·算法
颜酱2 小时前
前端算法必备:滑动窗口从入门到很熟练(最长/最短/计数三大类型)
前端·后端·算法