react Effect副作用 - 避免滥用Effect

react Effect副作用 - 避免滥用Effect

  • react Effect副作用
    • 基础概率
      • 什么是纯函数? 什么是副作用函数?
        • 纯函数
        • 副作用函数
      • 什么时候使用Effect
      • 如何使用Effect
    • 避免滥用Effect
      • 根据 props 或 state 来更新 state
      • 当 props 变化时重置所有 state
      • 将数据传递给父组件
      • 获取异步数据

react Effect副作用

基础概率

阅读文章
React新文档:不要滥用effect哦
react 中文文档

什么是纯函数? 什么是副作用函数?

纯函数

仅执行计算操作,不做其他操作,这类函数通常被称为纯函数

纯函数的特征
1.只负责自己的任务,它不会更改在该函数调用前就已存在的对象或变量。
2.输入相同,则输出相同,给定相同的输入,纯函数应总是返回相同的结果。

React 便围绕着这个概念进行设计,假设编写的所有组件都是纯函数。

React渲染过程必须自始至终是纯粹的,不改变在渲染前,就已存在的任何对象或变量。 – 这将会使其变得不纯粹,也就是我们说的产生副作用

/*案例1:不纯粹组件的写法该组件正在读写其外部声明的 guest 变量。这意味着 多次调用这个组件会产生不同的 JSX!并且,如果 其他 组件读取 guest ,它们也会产生不同的 JSX,其结果取决于它们何时被渲染!这是无法预测的。
*/
let guest = 0;function Cup() {// Bad:正在更改预先存在的变量!guest = guest + 1;return <h2>Tea cup for guest #{guest}</h2>;
}export default function TeaSet() {return (<><Cup /><Cup /><Cup /></>);
}// 案例2:纯粹组件的写法
function Cup({ guest }) {return <h2>Tea cup for guest #{guest}</h2>;
}export default function TeaSet() {return (<><Cup guest={1} /><Cup guest={2} /><Cup guest={3} /></>);
}

React 提供了 “严格模式”,在严格模式下开发时,会调用每个组件函数两次。通过重复调用组件函数,严格模式有助于找到违反这些规则的组件。严格模式在生产环境下不生效,因此不会降低应用程序的速度。如需引入严格模式,可以用 <React.StrictMode> 包裹根组件。

副作用函数

在 React 中,副作用通常属于 事件处理程序。事件处理程序是 React 在你执行某些操作(如单击按钮)时运行的函数。即使事件处理程序是在组件内部定义的,它们也不会在渲染期间运行! 因此事件处理程序无需是纯函数

可以理解副作用为:额外发生的事情,与渲染过程无关。

如果无法为副作用找到合适的事件处理程序,可以选择使用useEffect

什么时候使用Effect

React中有两个重要的概念

  • Rendering code渲染代码是不带副作用的纯函数,开发者编写的组件渲染逻辑,最终会返回一段JSX。
    // 渲染代码
    function App() {
    const [age, setAge] = useState(10);
    return <div>{age}</div>;
    }// 包含副作用:在 React 中,JSX 的渲染必须是纯粹操作,不应该包含任何像修改 DOM 的副作用。
    import { useState, useRef, useEffect } from 'react';
    function VideoPlayer({ src, isPlaying }) {const ref = useRef(null);
    if (isPlaying) {ref.current.play();  // 渲染期间不能调用 `play()`,获取不到ref.current的值。
    } else {ref.current.pause(); // 同样,调用 `pause()` 也不行。
    }
    return <video ref={ref} src={src} loop playsInline />;
    }
    
  • Event handlers事件处理器是组件内部的函数,用于执行用户操作,可以包含副作用。
    function App() {const [age, setAge] = useState(10);const changAge = () => {setAge(11);}
    return <div onClick={changAge }>{age}</div>;
    }
    

但是并不是所有副作用都能在Event handlers事件处理器中解决,比如初始化进入页面之后需要请求数据,也就是说不是由用户触发的可以让useEffect处理。

所以使用Event handlers还是useEffect的一个思路是:判断需求是否由用户行为触发

如何使用Effect

每个React组件都经历相同的生命周期
1.当组件被添加到屏幕上时,会进行组件的 挂载
2.当组件接收到新的 props state 时,通常是作为对交互的响应,它会进行组件的更新
3.当组件从屏幕上移除时,它会进行组件的卸载

使用说明
1.useEffect的参数只能是一般函数,不能是异步函数(async)。如果在useEffect里使用异步函数请求数据,需要其外部包装一个一般函数并调用。
2.默认情况下,Effect会在每次渲染后都会执行。如果添加依赖,当依赖发生变化时Effect会执行。
3.useEffect参数函数会在组件每次渲染完毕(dom渲染完毕)后执行。
在这里插入图片描述
4.在useEffect的回调函数中,可以返回一个函数,该函数被称为清理函数,该函数会在下次Effect执行前调用。可以在清理函数中,清除上一次Effect执行所带来的影响。

// 初始化 先Effect回调再清理函数
// 其他情况,先清理函数再Effect
const [keyword,setKeyword] = useState();useEffect(()=>{ const timer = setTimeout();//初始化时,先设置一个定时器A// 清理函数return ()=>{/*这里形成了一个闭包,timer是定时器A的值。下一次Effect执行前,先清理定时器A再生成新的定时器*/clearTimeout(timer);     }},[keyword])
}

避免滥用Effect

核心:Effect通常用于暂时“跳出” React 代码并与一些外部系统进行同步。
思路
1.能使用Event handlers的优先使用Event handlersuseEffect是最后的选择。
2.可以在渲染时期进行的计算,就在渲染期间执行。

根据 props 或 state 来更新 state

先是用 fullName 的旧值执行了整个渲染流程,然后useEffect修改了fullName立即使用更新后的值又重新渲染了一遍。

function Form() {const [firstName, setFirstName] = useState('Taylor');const [lastName, setLastName] = useState('Swift');// 🔴 避免:多余的 state 和不必要的 Effectconst [fullName, setFullName] = useState('');useEffect(() => {setFullName(firstName + ' ' + lastName);}, [firstName, lastName]);// ...
}

如果一个值可以基于现有的 propsstate 计算得出,不要把它作为一个 state,而是在渲染期间直接计算这个值。

function Form() {const [firstName, setFirstName] = useState('Taylor');const [lastName, setLastName] = useState('Swift');// ✅ 非常好:在渲染期间进行计算const fullName = firstName + ' ' + lastName;// ...
}

当 props 变化时重置所有 state

一个ProfilePage组件,它接收一个userId代表当前正在操作的用户,里面有一个评论输入框,用一个state来记录输入框中的内容。为了防止切换用户后,原用户输入的内容被当前的用户发出这种误操作,有必要在userId改变时置空state,包括ProfilePage组件的所有子组件中的评论state

export default function ProfilePage({ userId }) {const [comment, setComment] = useState('');// 🔴 避免:当 prop 变化时,在 Effect 中重置 stateuseEffect(() => {setComment('');}, [userId]);// ...
}

当在相同的位置渲染相同的组件时,React 会保留状态。通过组件的key来判断当前的组件是否相同,每当 key(这里是 userId)变化时,React 将重新加载组件。

export default function ProfilePage({ userId }) {return (<ProfileuserId={userId}key={userId}/>);
}function Profile({ userId }) {// ✅ 当 key 变化时,该组件内的 comment 或其他 state 会自动被重置const [comment, setComment] = useState('');// ...
}

将数据传递给父组件

父组件:将修改state的方法传递给子组件
子组件:调用修改state的方法

在 React 中,数据从父组件流向子组件,当子组件在Effect 中更新其父组件的 state 时,数据流变得非常难以追踪。

// 不是很理解怎么会有这种写法??
function Parent() {const [data, setData] = useState(null);// ...return <Child onFetched={setData} />;
}function Child({ onFetched }) {const data = useSomeAPI();// 🔴 避免:在 Effect 中传递数据给父组件useEffect(() => {if (data) {onFetched(data);}}, [onFetched, data]);// ...
}

如果组件和父组件都需要相同的数据,那么可以让父组件获取那些数据,并将其向下传递给子组件

function Parent() {const data = useSomeAPI();// ...// ✅ 非常好:向子组件传递数据return <Child data={data} />;
}function Child({ data }) {// ...
}

获取异步数据

非常常见的一种写法是在effect中异步获取数据,但这种代码存在一个问题
假设快速地输入 “hello”。那么 query 会从 “h” 变成 “he”,“hel”,“hell” 最后是 “hello”。这会触发一连串不同的数据获取请求,但无法保证对应的返回顺序。例如,“hell” 的响应可能在 “hello” 的响应 之后 返回。这种情况被称为 竞态条件:两个不同的请求 “相互竞争”,并以与你预期不符的顺序返回。

function SearchResults({ query }) {const [results, setResults] = useState([]);const [page, setPage] = useState(1);useEffect(() => {// 🔴 避免:没有清除逻辑的获取数据fetchResults(query, page).then(json => {setResults(json);});}, [query, page]);function handleNextPageClick() {setPage(page + 1);}// ...
}

可以给Effect添加一个清理函数,来忽略较早的返回结果。下面的案例采用一个变量ignore来控制这个Effect回调的"有效性",只要是执行了下一个Effect回调,上一个的ignore就变成了true,此时如果刚好上一个Effect的请求结束,由于ignore=true会跳过setResults

function SearchResults({ query }) {const [results, setResults] = useState([]);const [page, setPage] = useState(1);useEffect(() => {let ignore = false;fetchResults(query, page).then(json => {if (!ignore) {setResults(json);}});return () => {ignore = true;};}, [query, page]);function handleNextPageClick() {setPage(page + 1);}// ...
}

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

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

相关文章

关于配置深度学习虚拟环境(conda)的一些概念和常用命令

一、概念 下面介绍显卡驱动&#xff08;例NVIDIA GeForce&#xff09;、CUDA、cuDNN 的概念和联系。 显卡驱动 显卡驱动是封装成软件的程序&#xff0c;硬件厂商根据操作系统编写的配置文件。安装成功后成为操作系统中的一小块代码。它是硬件所对应的软件&#xff0c;计算机有…

QT学习(1)——创建第一个QT程序,信号和槽,打开关闭窗口的案例

目录 引出规范和帮助文档创建第一个Qt程序对象树概念信号signal槽slot自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重载带参数的按钮触发信号触发信号拓展 lambda表达式返回值mutable修饰案例 打开关闭窗口案例 总结 引出 QT学习&#xff08;1&#…

Android 集成Bugly完成线上的异常Exception收集及处理

文章目录 &#xff08;一&#xff09;添加产品APP&#xff08;二&#xff09;集成SDK&#xff08;三&#xff09;参数配置权限混淆 &#xff08;四&#xff09;初始化 &#xff08;一&#xff09;添加产品APP 一&#xff09;在个人头像 -> 我的头像 -> 新建产品 二&…

如何在 Mac 上恢复已删除的文件

点击“删除”后立即后悔&#xff1f;不用担心。我们的教程介绍了如何恢复已删除的 Mac 文件、电子邮件、iTunes 音乐等&#xff0c;即使您没有 Time Machine 备份并且无需支付软件费用。 在 macOS 中丢失文件可能会非常痛苦&#xff0c;如果您是点击删除的人&#xff0c;情况会…

关于vs2019 c++ STL 中容器的迭代器的 -> 运算符的使用,以 list 双向链表为例

&#xff08;1&#xff09;如下的结构体 A &#xff0c;若有指针 p new A() &#xff1b;则可以使用 p->m &#xff0c; p->n 解引用运算符。 struct A { int m ; int n; } 对于 STL 中提供的迭代器&#xff0c;提供了类似于指针的功能。对迭代器也可以使用 -> 运算…

【应用案例】Trinamic全闭环步进驱控方案

大家都很熟悉了传统的步进电机控制方式。如果在电机控制中采用反馈装置或者无传感器控制方式&#xff0c;将可以为那些对安全性、可靠性和精度有较高要求的运动控制应用提供更经济的选择。 大部分基于步进电机的运动系统运行在开环状态下&#xff0c;因此能够提供低成本的解决…

机器学习 - 集成学习算法介绍

集成学习的定义 集成学习&#xff08;Ensemble Learning&#xff09;是一种通过组合多个模型来提升预测性能的技术。简单来说&#xff0c;它就像是在开会时听取多人的意见&#xff0c;而不是只依赖一个人的观点&#xff0c;从而做出更准确的决策。 1. Bagging&#xff08;Boo…

SL3038 48V/60V电动车里程增程器电源驱动芯片 大电流3A

在电动车领域中&#xff0c;电池续航能力一直是制约其广泛应用的关键因素之一。为了提高电动车的续航能力和使用效率&#xff0c;各大厂商纷纷投入研发&#xff0c;寻求更为先进的电源驱动芯片解决方案。其中&#xff0c;SL3038 48V/60V电动车里程增程器电源驱动芯片以其卓越的…

【Java基础】权限修饰符

一个java文件中只能有一个被public修饰的类&#xff0c;且该类名与java文件的名字一样 同一个类同一个包不同包有继承不同包无继承private✔❌❌❌默认✔✔❌❌protected✔✔✔❌public✔✔✔✔

产品经理资料包干货

1.《产品汪》免费电子书 2016年我面试了差不多有200多位产品求职者&#xff0c;其中不乏之前做厨师编剧这些岗位的人。在这个过程中我意识到大众或许对产品经理这个岗位存在一些认知和理解上的误差&#xff0c;于是我就想着写一本产品经理相关的书。 关于本书的更多信息可查看…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷5(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

windows无法启动硬件设备,代码(19)解决办法

遇到一台电脑&#xff0c;摄像头无法使用&#xff0c;查看设备管理器看到设备名前出现感叹号&#xff0c;双击打开看到“由于其配置信息&#xff08;注册表中的&#xff09;不完整或已损坏&#xff0c;windows无法启动硬件设备&#xff0c;代码&#xff08;19&#xff09;”错误…

在js中table表格中进行渲染轮播图

效果图&#xff1a;示例&#xff1a; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title><script src"js/jquery-3.6.3.js"></script><style>/* 轮播图 */.basko {width: 100%;h…

优化Mac系统,TinkerTool一键掌控!

TinkerTool System for Mac是一款专为Mac用户设计的系统设置维护工具。这款软件不仅具备执行系统维护任务的能力&#xff0c;如管理文件和调整系统或用户设置&#xff0c;还包含一系列功能强大的工具&#xff0c;旨在帮助用户解决、修复、排除故障或修复系统错误和损坏的帐户等…

华为涅槃,余承东重生

最近一段时间&#xff0c;余承东甚为低调。最为明显的是&#xff0c;“遥遥领先”已经听不到了&#xff0c;“余大嘴”口中的措辞越来越克制。 今后手机相关的发布会&#xff0c;或许不再看到余承东的身影。 5月10日&#xff0c;余承东的职位正式更新&#xff0c;从终端BG CE…

Linux/ubuntu build编译make时出现has modification time int the future的问题解决方法

针对Linux由于双系统之间的时间冲突导致linux时间经常变化&#xff0c;出现执行make命令时出现“make[2]: Warning: File xxx.c’ has modification time 1.6e05 s in the future “警告的问题&#xff0c;亦或者虚拟机出现相同的问题。 由于时钟同步问题&#xff0c;出现 warn…

菲律宾签证照片尺寸要求,用手机生成

菲律宾签证照片尺寸要求如下图所示&#xff0c;可以用手机在微信搜索随时照小程序&#xff0c;快速生成哦。

【Android Studio】使用UI工具绘制,ConstraintLayout 限制性布局,快速上手

文章目录 一、前言二、绘制效果三、ConstraintLayout 使用方法3.1 创建布局文件3.2 替换配置3.3 设置约束&#xff0c;步骤13.4 设置约束&#xff0c;步骤23.5 其他设置 四、结束 一、前言 在进行Android APP开发过程中&#xff0c;减少layout嵌套即可改善UI的绘制性能&#x…

vue 百度地图点击marker修改marker图片,其他marker图片不变。

解决思路&#xff0c;就是直接替换对应marker的图片。获取marker对象判断点击的marker替换成新图片&#xff0c;上一个被点击的就替换成老图片。 marker.name tag;marker.id i; //一定要设置id&#xff0c;我这里是设置的循环key值&#xff0c;要唯一性。map.addOverlay(mark…

【ARM Cortex-M 系列 2.3 -- Cortex-M7 Debug event 详细介绍】

请阅读【嵌入式开发学习必备专栏】 文章目录 Cortex-M7 Debug eventDebug events Cortex-M7 Debug event 在ARM Cortex-M7架构中&#xff0c;调试事件&#xff08;Debug Event&#xff09;是由于调试原因而触发的事件。一个调试事件会导致以下几种情况之一发生&#xff1a; 进…