react中hooks之useEffect 用法总结

1. 什么是函数的副作用(Side Effects)

副作用是指在组件渲染过程中,除了返回 JSX 之外的其他操作,例如:

  • 数据获取(API 调用)
  • 订阅数据源
  • 手动修改 DOM
  • 设置定时器
  • 存储数据
  • 日志记录
    纯函数是特定的输入只会有特定的输出,也就是说组件会输出特定的DOM给浏览器渲染,除去这份逻辑以外的操作就称之为副作用,比如获取数据,监听,订阅等等

2. useEffect 的执行时机

2.1 省略依赖项

useEffect(() => {
  console.log('每次渲染都会执行');
}); // 没有依赖项数组
  • 组件每次渲染都会执行
  • 包括首次渲染和后续更新

2.2 指定依赖项

useEffect(() => {
  console.log(`count 发生变化:${count}`);
}, [count]); // 依赖于 count
  • 首次渲染时执行
  • 依赖项发生变化时执行
  • 多个依赖项时,任意一个变化都会触发执行

2.3 空数组依赖项

useEffect(() => {
  console.log('只在组件挂载时执行一次');
}, []); // 空数组
  • 仅在组件首次渲染(挂载)时执行一次
  • 类似于 class 组件的 componentDidMount

3. 常见问题和最佳实践

3.1 避免依赖项循环

// ❌ 错误示例:造成无限循环
function BadExample() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    setCount(count + 1); // 直接修改依赖项
  }, [count]);
  
  return <div>{count}</div>;
}

// ✅ 正确示例:使用函数式更新
function GoodExample() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    setCount(prevCount => prevCount + 1);
  }, []); // 不需要依赖项
  
  return <div>{count}</div>;
}

3.2 分离关注点

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);

  // ✅ 分开声明不同功能的 useEffect
  useEffect(() => {
    // 获取用户信息
    fetchUser(userId).then(setUser);
  }, [userId]);

  useEffect(() => {
    // 获取用户帖子
    fetchUserPosts(userId).then(setPosts);
  }, [userId]);

  return (
    <div>
      <UserInfo user={user} />
      <UserPosts posts={posts} />
    </div>
  );
}

4. 清除副作用

4.1 清理函数的执行时机

清理函数会在以下情况执行:

  • 组件卸载时
  • 下一次 effect 执行前

4.2 事件监听示例

function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    // 添加事件监听
    window.addEventListener('resize', handleResize);
    
    // 清理函数
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 空依赖数组,只在挂载和卸载时执行

  return <div>Window width: {width}</div>;
}

4.3 定时器示例

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);

    // 清理函数:组件卸载时清除定时器
    return () => clearInterval(timer);
  }, []); // 空依赖数组

  return <div>Count: {count}</div>;
}

4.4 数据订阅示例

function DataSubscriber({ dataSource }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    let isSubscribed = true;

    const handleData = (newData) => {
      if (isSubscribed) {
        setData(newData);
      }
    };

    // 订阅数据源
    const subscription = dataSource.subscribe(handleData);

    // 清理函数:取消订阅
    return () => {
      isSubscribed = false;
      subscription.unsubscribe();
    };
  }, [dataSource]); // 依赖于 dataSource

  return <div>{data ? <DataView data={data} /> : 'Loading...'}</div>;
}

4.5 WebSocket 连接示例

function WebSocketComponent({ url }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const ws = new WebSocket(url);

    ws.onmessage = (event) => {
      setMessages(prev => [...prev, event.data]);
    };

    // 清理函数:关闭 WebSocket 连接
    return () => {
      ws.close();
    };
  }, [url]);

  return (
    <div>
      {messages.map((msg, index) => (
        <div key={index}>{msg}</div>
      ))}
    </div>
  );
}

5. 实际应用场景

5.1 表单自动保存

function AutoSaveForm() {
  const [content, setContent] = useState('');
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    // 防抖处理
    const timeoutId = setTimeout(() => {
      if (content) {
        setSaving(true);
        saveContent(content)
          .then(() => setSaving(false));
      }
    }, 1000);

    return () => clearTimeout(timeoutId);
  }, [content]);

  return (
    <div>
      <textarea
        value={content}
        onChange={e => setContent(e.target.value)}
      />
      {saving && <span>Saving...</span>}
    </div>
  );
}

5.2 实时搜索

function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  useEffect(() => {
    // 避免空查询
    if (!query.trim()) {
      setResults([]);
      return;
    }

    const abortController = new AbortController();

    async function fetchResults() {
      try {
        const response = await fetch(
          `/api/search?q=${query}`,
          { signal: abortController.signal }
        );
        const data = await response.json();
        setResults(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          // 忽略中止的请求错误
          return;
        }
        console.error('搜索出错:', error);
      }
    }

    const timeoutId = setTimeout(fetchResults, 300);

    // 清理函数:取消请求和清除定时器
    return () => {
      clearTimeout(timeoutId);
      abortController.abort();
    };
  }, [query]);

  return (
    <div>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="搜索..."
      />
      <ul>
        {results.map(result => (
          <li key={result.id}>{result.title}</li>
        ))}
      </ul>
    </div>
  );
}

6. 最佳实践总结

  1. 保持 effect 函数简洁,专注于单一功能
  2. 合理使用依赖项,避免不必要的执行
  3. 始终清理副作用,防止内存泄漏
  4. 使用条件语句控制 effect 的执行
  5. 考虑使用自定义 Hook 封装常用的副作用逻辑
  6. 在开发环境下使用 ESLint 的 exhaustive-deps 规则检查依赖项
  7. 使用 useCallback 和 useMemo 优化依赖项

通过合理使用 useEffect,我们可以优雅地处理组件的副作用,实现更复杂的交互逻辑,同时保持代码的可维护性和性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954534.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

海豚调度DolphinScheduler-3.1.9配置windows本地开发环境

源代码下载地址https://dolphinscheduler.apache.org/zh-cn/docs/3.1.9 1.Zookeeper安装与使用 如图下载解压zookeeper安装包&#xff0c;并创建data和log目录 下载地址 https://archive.apache.org/dist/zookeeper/zookeeper-3.6.4/apache-zookeeper-3.6.4-bin.tar.gz 进入…

springCloudGateway+nacos自定义负载均衡-通过IP隔离开发环境

先说一下想法&#xff0c;小公司开发项目&#xff0c;参考若依框架使用的spring-cloud-starter-gateway和spring-cloud-starter-alibaba-nacos, 用到了nacos的配置中心和注册中心&#xff0c;有多个模块&#xff08;每个模块都是一个服务&#xff09;。 想本地开发&#xff0c;…

大模型训练_硬件微调知识增强

目录 关键硬件 大模型类型 垂域训练技术 微调技术 领域大模型训练trick 知识增强 关键硬件 GPU GPU擅长处理图形渲染和数据并行任务&#xff0c;可以同时处理大量的矩阵运算&#xff0c;在科学计算、人工智能、游戏开发等领域应用广泛。 显卡 显卡是一种完整的硬件设…

linux分配磁盘空间命令

使用命令lsblk查询linux磁盘空间时&#xff0c;发现空间并没有被分配完 如图&#xff0c;600G&#xff0c;但实际分配了一共199G&#xff0c;剩余500G&#xff0c;我们需要通过命令进行剩余存储的分配。 思路&#xff1a;创建新的分区->更新内核分区表->初始化新分区作…

【运维自动化-作业平台】魔法变量到底如何使用之主机列表类型

蓝鲸作业平台&#xff0c;以下简称作业平台或JOB平台 魔法变量&#xff1a;JOB平台执行引擎提供的特有的变量能力用法 脚本中使用&#xff0c;并且需要事先声明&#xff1a;job_import {{变量名}} 声明后&#xff0c;同样是使用 dollar 符 大括号&#xff1a;${变量名}来取值…

Windows重装后NI板卡LabVIEW恢复正常

在重新安装Windows系统后&#xff0c;NI&#xff08;National Instruments&#xff09;板卡能够恢复正常工作&#xff0c;通常是由于操作系统的重新配置解决了之前存在的硬件驱动、兼容性或配置问题。操作系统重装后&#xff0c;系统重新加载驱动程序、清理了潜在的冲突或损坏的…

Docker启动达梦 rman恢复

目录标题 1. 主库备份2. Docker启动备库3. 备库修改属组4. 开始恢复5. 连接数据库配置归档 & Open6. 检查数据 关于达梦数据库&#xff08;DMDBMS&#xff09;的主库备份、Docker启动备库、恢复备份以及配置归档和打开数据库的详细步骤。 1. 主库备份 # 使用达梦数据库备…

【C语言】_字符串拷贝函数strcpy

目录 1. 函数声明及功能 2. 使用示例 3. 注意事项 4. 模拟实现 4.1 第一版&#xff1a;基本功能判空const修饰 4.2 第二版&#xff1a;优化对于\0的单独拷贝 4.3 第三版&#xff1a;仿strcpy的char*返回值 1. 函数声明及功能 char * strcpy ( char * destination, cons…

XML序列化和反序列化的学习

1、基本介绍 在工作中&#xff0c;经常为了调通上游接口&#xff0c;从而对请求第三方的参数进行XML序列化&#xff0c;这里常使用的方式就是使用JAVA扩展包中的相关注解和类来实现xml的序列化和反序列化。 2、自定义工具类 import javax.xml.bind.JAXBContext; import javax.x…

js使用qrcode与canvas生成带logo的二维码

qrcode库 文档 https://www.npmjs.com/package/qrcode 安装 npm i qrcode 使用 errorCorrectionLevel: H // 容错率&#xff08;H是最高&#xff0c;其它看文档&#xff09; width: 200 // 大小 margin: 2 // 边距 import QRCode from qrcodeconst testFn async () > {c…

【计算机网络】lab5 ARP协议

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;计算机网络_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2.…

【React】脚手架进阶

目录 暴露webpack配置package.json的变化修改webpack.config.js配置less修改域名、端口号浏览器兼容处理处理跨域 暴露webpack配置 react-scripts对脚手架中的打包命令进行封装&#xff0c;如何暴露这些打包配置呢&#xff1f;上篇写到在package.json中的scripts配置项中有eje…

java项目之现代企业人力资源管理系统设计与实现(源码+文档)

大家好我是风歌&#xff0c;今天要和大家聊的是一款基于ssm的现代企业人力资源管理系统设计与实现。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 现代企业人力资源管理系统设计与实现的主要使用者分为管理员、经理和普通员工三个角…

2025.1.15——三、报错注入

一、基本操作&#xff1a;整理已知信息&#xff0c;本题为报错注入&#xff0c;需进一步确认回显方式 二、用updatexml()解题步骤 step 1&#xff1a;依据回显方式判断题目类型 键入&#xff1a;1、1 and 11 、id2-1 得到&#xff1a;查询正确的回显 键入&#xff1a;1’、…

【IDEA 2024】学习笔记--文件选项卡

在我们项目的开发过程中&#xff0c;由于项目涉及的类过多&#xff0c;以至于我们会打开很多的窗口。使用IDEA默认的配置&#xff0c;个人觉得十分不便。 目录 一、设置多个文件选项卡按照文件字母顺序排列 二、设置多个文件选项卡分行显示 一、设置多个文件选项卡按照文件字…

自己动手搭建“接入 AI Agent 的数字人”

前言 本文的实战案例来自于开源项目&#xff1a;https://github.com/wan-h/awesome-digital-human-live2d。该项目可以运用Dify编排框架和Live2D驱动模型搭建智能数字人&#xff0c;实现智能对话并动态交互&#xff0c;大家可以自行部署尝试&#xff0c;项目效果如下。 开源&a…

Flink链接Kafka

一、基于 Flink 的 Kafka 消息生产者 Kafka 生产者的创建与配置&#xff1a; 代码通过 FlinkKafkaProducer 创建 Kafka 生产者&#xff0c;用于向 Kafka 主题发送消息。Flink 执行环境的配置&#xff1a; 配置了 Flink 的检查点机制&#xff0c;确保消息的可靠性&#xff0c;支…

电脑有两张网卡,如何实现同时访问外网和内网?

要是想让一台电脑用两张网卡&#xff0c;既能访问外网又能访问内网&#xff0c;那可以通过设置网络路由还有网卡的 IP 地址来达成。 检查一下网卡的连接 得保证电脑的两张网卡分别连到外网和内网的网络设备上&#xff0c;像路由器或者交换机啥的。 给网卡配上不一样的 IP 地…

dockerfile2.0

dockerfile实现lnmp nginx centos7 mysql centos7 php centos7 自定义镜像来实现整个架构 cd /opt mkdir nginx mysql php cd nginx 拖入nginx和wordpress vim Dockerfile vim nginx.conf ↓ worker_processes 1; events {worker_connections 1024; } http {include …

mysql-5.7.18保姆级详细安装教程

本文主要讲解如何安装mysql-5.7.18数据库&#xff1a; 将绿色版安装包mysql-5.7.18-winx64解压后目录中内容如下图&#xff0c;该例是安装在D盘根目录。 在mysql安装目录中新建my.ini文件&#xff0c;文件内容及各配置项内容如下图&#xff0c;需要先将配置项【skip-grant-tab…