react Effect副作用 - 避免滥用Effect

news2025/1/23 10:46:01

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 和不必要的 Effect
  const [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 中重置 state
  useEffect(() => {
    setComment('');
  }, [userId]);
  // ...
}

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

export default function ProfilePage({ userId }) {
  return (
    <Profile
      userId={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.coloradmin.cn/o/1673472.html

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

相关文章

​民兵档案管理系统-退伍军人档案管理全流程追踪

民兵档案管理系统-退伍军人档案管理全流程追踪 民兵档案管理系统&#xff08;智档案DW-S403&#xff09;是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 RFID档案管理系统是以先进…

代码随想录——N叉树的层序遍历(Leetcode429)

题目链接 层序遍历 /* // Definition for a Node. class Node {public int val;public List<Node> children;public Node() {}public Node(int _val) {val _val;}public Node(int _val, List<Node> _children) {val _val;children _children;} }; */class Sol…

硬件FMEA与软件FMEA的区别——FMEA软件

​免费试用FMEA软件-免费版-SunFMEA 在产品开发和制造过程中&#xff0c;失效模式与影响分析&#xff08;FMEA&#xff09;作为一种预防性的质量工具&#xff0c;对于确保产品性能和质量至关重要。然而&#xff0c;硬件FMEA和软件FMEA在应用和实践方面存在显著的区别。本文旨在…

Excel 根据包含的关键词将指定列按关键词指定顺序排列

例题描述和简单分析 有 Excel 文件&#xff0c;数据如下所示&#xff1a; AB1Parent ColumnModifier (Column)2Jack lives in the villageRose3As mentioned by jackVillage4Rose already spoke to jack about last nightJack5Rose left the village6rose was their yesterda…

uboot 顶层 Makefile 逐行分析

文章目录 0001-00080009-00180019-00510052-00920093-01070108-01230124-01770178-23150178-01810182-01860187-02020203-02450246-02620263-02720273-03370338-03830384-03870388-04250426-04490450-04740475-04860487-04980499-05340535-05500551-05650566-221822192220-2332…

vue3延迟加载(异步组件​)defineAsyncComponent

最简单用法 Index.vue: <script setup> import { onMounted, defineAsyncComponent } from vue import ./index.cssconst Child defineAsyncComponent(() > import(./Child.vue))onMounted(() > {}) </script><template><div class"m-home-w…

HTML静态网页成品作业(HTML+CSS)——动漫哆啦A梦网页(3个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有3个页面。 二、作品演示 三、代…

等保2.0的全面解读与实施策略

《网络安全等级保护基本要求》&#xff08;等保2.0&#xff09;是中华人民共和国国家安全部于2019年6月发布的网络安全等级保护标准。该标准规定了我国关键信息基础设施的网络安全等级保护要求和评估标准&#xff0c;对于保障我国网络安全具有重要的意义。下面是对等保2.0的全面…

功能安全如何在公司顺利开展?-亚远景科技

亚远景功能安全主题线上会议报名开启&#xff01; 随着汽车技术的不断发展&#xff0c;汽车系统的复杂性和交互性大幅增加&#xff0c;功能安全成为确保驾驶员、乘客及行人安全的关键。 本场功能安全线上会议&#xff0c;亚远景为汽车行业的相关人员准备了以下内容&#xff1a…

保姆级AI绘画入门教程,从此找图不求人、风格还统一(入门篇)

安装流程 安装包使用的是b站大佬“秋葉aaaki”制作的整合程序。他还会讲授一些lora训练的方法&#xff08;对于大多数人来说可能一生都用不上的功能&#xff0c;跟着月仔学学基础操作就成了&#xff0c;哈哈。&#xff09; 首先要下载安装包&#xff0c;最新的安装包大小已经…

vant showNotify样式修改

main.ts直接引入根css会影响已经修改好的其他vant样式,直接添加一个新的my-vant.scss文件引入 //vant showNotify样式修改 .van-popup.van-popup--top.van-notify {width: 343px !important;height: 40px !important;border-radius: 12px !important;margin: 0 auto !importan…

黑马点评项目总结及个人优化

怎么根据前端代码实现自己的后端业务,实现不同接口 查阅文档:如果有完善的接口文档,可以直接查阅文档来了解后端所有接口的业务逻辑和功能。 阅读后端代码:通过阅读后端代码,特别是控制器(Controller)层和服务(Service)层的代码,可以了解后端所有接口的具体实现逻辑。…

FPGA verilog LVDS通信协议笔记

一幅图胜过千言万语 直接开始挫代码&#xff0c;先写top.v。 module top();reg clk; // 生成时钟的寄存器 reg rst; // 生成复位信号的寄存器initial clk 1; // 初始值取1 always #1 clk ~clk; //1ns取反一次initial begin // 复位信号&#xff0c;先0&#xff0c;过段时间赋…

飞书深诺沈菁:智能化是企业增长的助推器

刚刚结束的ChinaJoy&#xff0c;既是游戏行业的盛会&#xff0c;也是出海企业的盛会。活动期间&#xff0c;飞书深诺旗下专注于游戏出海的平台Meetgames举办「进无止境」Meetgames升级发布会&#xff0c;推出全新升级的Meetgames平台&#xff0c;并与合作伙伴和嘉宾共同探讨成长…

HBuilder简单实现打包

这里使用HBuilder的原生云打包来进行打包APP&#xff0c; 首先需要登录HBuilder开发者中心 确认无误后&#xff0c;回到HBuilder,点击发行&#xff0c; 填写好对应信息后即可进行下一步操作&#xff0c;其他选择默认就可以了&#xff0c; 选择无视风险&#xff0c;继续打包 到…

GO—web程序中的请求缓存设置

背景 假设用户数据存在数据库&#xff0c;现在需要一个函数&#xff0c;通过用户名称返回用户信息。 期望&#xff1a;在一次web请求中&#xff0c;不过调用多少次这个函数&#xff0c;只请求一次数据库。 基本信息 type User struct {Name stringAge int }func GetALLUser…

K8s源码分析(一)-K8s调度框架及调度器初始化介绍

本文首发在个人博客上&#xff0c;欢迎来踩&#xff01; 文章目录 调度框架介绍K8s scheduler 介绍K8s scheduler的初始化Cobra介绍K8s scheduler中初始化的源代码解析 调度框架介绍 这是官方对于v1.27调度框架的介绍文档&#xff1a;https://v1-27.docs.kubernetes.io/docs/…

开源模型应用落地-CodeQwen模型小试-集成langchain(四)

一、前言 通过学习代码专家模型&#xff0c;开发人员可以获得高效、准确和个性化的代码支持。这不仅可以提高工作效率&#xff0c;还可以在不同的技术环境中简化软件开发工作流程。代码专家模型的引入将为开发人员带来更多的机会去关注创造性的编程任务&#xff0c;从而推动软件…

kubernetes1.27.3版本单主机部署详细教程

本次kubernetes单主机部署是基于AnolisOS-7.9操作系统进行部署的&#xff0c;当然也适用于Centos7的操作系统&#xff0c;根据个人情况&#xff0c;选择适合自己的版本! 本次部署的kubernetes版本是1.27.3版本&#xff0c;系统架构是etcd&#xff0c;API Server&#xff0c;Con…

【Jenkins】Pipeline流水线语法解析全集 -- 声明式流水线

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…