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 钩子。这能提高代码的可读性,并使依赖项管理更加清晰。
相关推荐
云水边几秒前
前端网络性能优化
前端
用户51681661458415 分钟前
[微前端 qiankun] 加载报错:Target container with #child-container not existed while devi
前端
东北南西16 分钟前
设计模式-工厂模式
前端·设计模式
HANK22 分钟前
ECharts高效实现复杂图表指南
前端·vue.js
入秋25 分钟前
Linux服务器安装部署 Nginx、Redis、PostgreSQL、Docker
linux·前端
acocosum26 分钟前
毫米波雷达基础知识学习报告
前端
程序员鱼皮28 分钟前
这套 Java 监控系统太香了!我连夜给项目加上了
java·前端·ai·程序员·开发·软件开发
Juchecar31 分钟前
Vue3 响应式 ref 和 reactive 原理详解及选择建议
前端·vue.js
拾光拾趣录34 分钟前
JavaScript 究竟怎么跑
前端·javascript
Aotman_36 分钟前
el-input 重写带图标密码框(点击小眼睛显示、隐藏密码)
前端·javascript·vue.js