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([]);// ✅ 分开声明不同功能的 useEffectuseEffect(() => {// 获取用户信息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]); // 依赖于 dataSourcereturn <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><textareavalue={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><inputvalue={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,我们可以优雅地处理组件的副作用,实现更复杂的交互逻辑,同时保持代码的可维护性和性能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LSA更新、撤销

LSA的新旧判断&#xff1a; 1.seq&#xff0c;值越大越优先 2.chksum&#xff0c;值越大越优先 3.age&#xff0c;本地的LSA age和收到的LSA age作比较 如果差值<900s&#xff0c;认为age一致&#xff0c;保留本地的&#xff1a;我本地有一条LSA是100 你给的是400 差值小于…

【FlutterDart】MVVM(Model-View-ViewModel)架构模式例子-dio版本(31 /100)

动图更精彩 dio & http 在Flutter中&#xff0c;dio和http是两个常用的HTTP请求库&#xff0c;它们各有优缺点。以下是对这两个库的详细对比&#xff1a; 功能特性 http&#xff1a; 功能&#xff1a;提供了基本的HTTP请求和响应功能&#xff0c;如GET、POST、PUT、DELE…

递归40题!再见递归

简介&#xff1a;40个问题&#xff0c;有难有易&#xff0c;均使用递归完成&#xff0c;需要C/C的指针、字符串、数组、链表等基础知识作为基础。 1、数字出现的次数 由键盘录入一个正整数&#xff0c;求该整数中每个数字出现的次数。 输入&#xff1a;19931003 输出&#xf…

STM32 FreeRTOS 的任务挂起与恢复以及查看任务状态

目录 任务的挂起与恢复的API函数 任务挂起函数 任务恢复函数 任务恢复函数&#xff08;中断中恢复&#xff09; 函数说明 注意事项 查看任务状态 任务的挂起与恢复的API函数 vTaskSuspend()&#xff1a;挂起任务, 类似暂停&#xff0c;可恢复 vTaskResume()&#xff1a…

openharmony标准系统方案之瑞芯微RK3568移植案例

标准系统方案之瑞芯微RK3568移植案例 ​本文章是基于瑞芯微RK3568芯片的DAYU200开发板&#xff0c;进行标准系统相关功能的移植&#xff0c;主要包括产品配置添加&#xff0c;内核启动、升级&#xff0c;音频ADM化&#xff0c;Camera&#xff0c;TP&#xff0c;LCD&#xff0c…

sunrays-framework 微调

文章目录 1.common-log4j2-starter 动态获取并打印日志存储的根目录的绝对路径以及应用的访问地址1.目录2.log4j2.xml 配置LOG_HOME3.LogHomePrinter.java 配置监听器4.spring.factories 注册监听器5.测试1.common-log4j2-starter-demo 配置2.启动测试 2.common-minio-starter …

ElasticSearch上

安装ElasticSearch Lucene&#xff1a;Java语言的搜索引擎类库&#xff0c;易扩展&#xff1b;高性能&#xff08;基于倒排索引&#xff09;Elasticsearch基于Lucene&#xff0c;支持分布式&#xff0c;可水平扩展&#xff1b;提供Restful接口&#xff0c;可被任何语言调用Ela…

element-ui textarea备注 textarea 多行输入框

发现用这个组件&#xff0c;为了给用户更好的体验&#xff0c;要加下属性 1. 通过设置 autosize 属性可以使得文本域的高度能够根据文本内容自动进行调整&#xff0c;并且 autosize 还可以设定为一个对象&#xff0c;指定最小行数和最大行数。:autosize"{ minRows: 3, ma…

.netframwork模拟启动webapi服务并编写对应api接口

在.NET Framework环境中模拟启动Web服务&#xff0c;可以使用几种不同的方法。一个常见的选择是利用HttpListener类来创建一个简单的HTTP服务器&#xff0c;或者使用Owin/Katana库来自托管ASP.NET Web API或MVC应用。下面简要介绍Owin/Katana示例代码。这种方法更加灵活&#x…

路由环路的产生原因与解决方法(1)

路由环路 路由环路就是数据包不断在这个网络传输&#xff0c;始终到达不了目的地&#xff0c;导致掉线或者网络瘫痪。 TTL &#xff08;生存时间&#xff09;&#xff1a;数据包每经过一个路由器的转发&#xff0c;其数值减1&#xff0c;当一个数据包的TTL值为0是&#xff0c;路…

Android CustomTextField

在 Compose 中开发用户界面时&#xff0c;需要处理输入框和键盘的交互&#xff0c;例如在键盘弹出时调整布局位置&#xff0c;避免遮挡重要内容。本篇博客将通过一个完整的示例展示如何实现这一功能。 功能概述 本例实现了一个简单的输入框。当输入框获得焦点或输入文字时&…

Alluxio数据流转方案在联通智网的应用

分享嘉宾 陈得泳 - 中国联通大数据平台 SRE 工程师&#xff0c;致力于基于开源生态构建稳定、高效、安全、低成本的大数据集群。 观看完整分享回放 业务背景 统一底座和安全基座位于不同IDC&#xff1b;统一底座&#xff1a;承接 O 域全域网络数据&#xff0c;包括移动网信…

搜维尔科技提供完整的人形机器人解决方案以及训练系统

问题&#xff1a;从灵巧手收集的数据是否也会在大脑大模型中训练&#xff0c;或是在专门用于手部控制的单独模型中训练&#xff1f; Q: If the data collected from dexterous hands will be trained as well in the brain large model, or in a separate model dedicated for…

打造餐饮品牌的产品矩阵:美味与策略的完美融合-中小企实战运营和营销工作室博客

打造餐饮品牌的产品矩阵&#xff1a;美味与策略的完美融合-中小企实战运营和营销工作室博客 在竞争激烈的餐饮市场中&#xff0c;打造一个成功的餐饮品牌&#xff0c;关键在于构建一个强大且富有吸引力的产品矩阵。这不仅涉及到研发出令人垂涎欲滴的美味佳肴&#xff0c;更需要…

前端大数据处理 - Web Worker

前言 先了解一个概念&#xff1a;页面假死 浏览器有GUI渲染线程与JS引擎线程&#xff0c;这两个线程是互斥的关系 当js有大量计算时&#xff0c;会造成 UI 阻塞&#xff0c;出现界面卡顿、掉帧等情况&#xff0c;严重时会出现页面卡死的情况&#xff0c;俗称假死 在前端开发…

无缝过渡:将 Ansys 子结构模型转换为 Nastran

了解如何将 Ansys 子结构模型无缝转换为 Nastran&#xff0c;以满足有效载荷动态模型要求 Ansys 子结构模型的优势 Ansys 子结构模型为从事大型装配体结构分析和仿真的工程师和分析师提供了多项优势。 这些模型通过将复杂结构划分为更小、更易于管理的子结构&#xff0c;可以…

【Flink系列】4. Flink运行时架构

4. Flink运行时架构 4.1 系统架构 Flink运行时架构——Standalone会话模式为例 1&#xff09;作业管理器&#xff08;JobManager&#xff09; JobManager是一个Flink集群中任务管理和调度的核心&#xff0c;是控制应用执行的主进程。也就是说&#xff0c;每个应用都应该被…

AI刷题-饭馆菜品选择问题、构造回文字符串问题

目录 一、饭馆菜品选择问题 问题描述 测试样例 解题思路&#xff1a; 问题理解 数据结构选择 算法步骤 最终代码&#xff1a; 运行结果&#xff1a; 二、构造回文字符串问题 问题描述 测试样例 解题思路&#xff1a; 解题思路 具体步骤 最终代码&#xff1a;…

使用redis-cli命令实现redis crud操作

项目场景&#xff1a; 线上环境上redis中的key影响数据展示&#xff0c;需要删除。但环境特殊没办法通过 redis客户端工具直连。只能使用redis-cli命令来实现。 操作步骤&#xff1a; 1、确定redis安装的服务器&#xff1b; 2、找到redis的安装目录下 ##找到redis安装目…

讲一下ZooKeeper的持久化机制?

大家好&#xff0c;我是锋哥。今天分享关于【讲一下ZooKeeper的持久化机制&#xff1f;】面试题。希望对大家有帮助&#xff1b; 讲一下ZooKeeper的持久化机制&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 ZooKeeper 是一个开源的分布式协调服务&…