React中 类组件 与 函数组件 的区别

类组件 与 函数组件 的区别

  • 1. 类组件
  • 2. 函数组件
    • Hook
      • useState
      • useEffect
      • useCallback
      • useMemo
      • useContext
      • useRef
  • 3. 函数组件与类组件的区别
    • 3.1 表面差异
    • 3.2 最大不同
      • 原因

1. 类组件

在React中,类组件就是基于ES6语法,通过继承 React.component 得到的组件。

class Demo extends React.Component {// 初始化类组件的 statestate = {text: "111"};// 编写生命周期方法 didMountcomponentDidMount() {// 省略业务逻辑}// 编写自定义的实例方法changeText = (newText) => {// 更新 statethis.setState({text: newText});};// 编写生命周期方法 renderrender() {return (<div className="demoClass"><p>{this.state.text}</p><button onClick={() => this.changeText(222)}>点我修改</button></div>);}
}

2. 函数组件

在函数组件也称为无状态组件,顾名思义就是以函数形态存在的 React 组件。

hooks出现之前,react 中的函数组件通常只考虑负责UI的渲染,没有自身的状态,没有业务逻辑代码,是一个纯函数

下面这个函数组件就是一个纯函数,它的输出只由参数props决定,不受其他任何因素影响。

function DemoFunction(props) {const { text } = propsreturn (<div className="demoFunction"><p>{`function 组件所接收到的来自外界的文本内容是:[${text}]`}</p></div>);
}

但是这种函数组件一旦我们需要给组件加状态,那就只能将组件重写为类组件,因为函数组件没有实例,没有生命周期
所以我们说在 hook 之前的 函数组件和类组件最大的区别就是 状态的有无。

Hook

React Hooks 是从 React 16.8 版本推出的新特性,目的是解决 React 的状态共享以及组件生命周期管理混乱的问题。 React Hooks 的出现标志着,React 不会再存在无状态组件的情况,React 将只有类组件和函数组件的概念。

hooks为函数组件提供了状态,也支持在函数组件中进行数据获取、订阅事件解绑事件等等。

下面先介绍几个最基本的hook作为基础知识。

useState

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

通过 useState 为组件提供状态。useState 的参数是 state 的初始值,他只有在组件第一次渲染的时候会生效,他的返回值是一个数组,第一个是 state,第二个是设置state的函数。

useEffect

//1. 不设置依赖项数组,那么在组件挂载时和每次渲染时,都会执行一次副作用
useEffect(() => {console.log("useEffect副作用执行");//用setTimeout模拟ajax请求setTimeout(() => {setList(result);}, 3000);});
//2. 设置依赖项为空数组,那么只有在组件挂载和卸载时执行副作用
useEffect(() => {console.log("useEffect副作用执行");setTimeout(() => {setList(result);}, 3000);return ()=>{// 组件卸载前的操作}},[]);// 空的依赖数组确保只在组件挂载和卸载时执行一次
//3. 如果设置依赖项且不为空,那么在组件挂载时和数组中的依赖项发生变化时,副作用就会重新执行。useEffect(() => {console.log("useEffect副作用执行");setTimeout(() => {setList(result);}, 3000);}, [list]);

useEffect用于处理组件的副作用

  • 第一个参数是一个回调函数,在里面进行业务逻辑代码的书写(副作用操作)
    • 通常在副作用中进行ajax请求,事件的绑定与解绑,设置定时器与清除等等。
  • 第二个参数是依赖项数组,指定副作用函数的触发条件。
    • 如果不设置第二个参数,那么当该组件挂载和组件每渲染一次,副作用就会执行一次
    • 如果数组中的依赖设置为,那么只会在组件挂载和卸载时执行一次副作用。在 useEffect 的回调函数中,可以执行组件挂载时的操作,并在返回的清理函数中执行组件卸载前的操作。
    • 如果数组中存在依赖项,当组件挂载时和依赖项数组中的依赖发生变化,那么该副作用就会重新执行

useCallback

用于缓存函数,以避免无效的函数重新创建。 第一个参数为要缓存的函数,第二个参数为依赖项数组,如果依赖发生了变化,那么就会生成一个新的函数;否则当组件重新渲染时,不会重新定义这个函数,而是会取缓存。

useMemo

用于缓存计算结果,以避免无效的计算重复执行。 第一个参数为要缓存的函数(注意实际被缓存的是函数被执行过后的值),第二个参数为依赖项数组,如果依赖发生了变化,那么就会重新执行这个函数,得到新的返回值;否则当组件重新渲染时,不会重新执行这个函数,而是直接取被缓存的该函数的返回值。

useCallbackuseMemo 都是在函数式组件中用作优化性能的。

默认情况下,当一个组件重新渲染时, React 将递归渲染它的所有子组件。组件中的函数就会重新被创建。因为在 JavaScript 中,function () {} 或者 () => {} 总是会生成不同的函数。

  • useCallback
    • 避免在内部编写额外的嵌套函数
    • 避免组件的重新渲染

比如说在父组件中接收的props有{ A,B,C },函数handleSubmit()依赖于A和B,将这个函数作为子组件的props),如果C发生变化,会导致重新创建一个函数handleSumit(),也就导致子组件的props是不同,从而引起子组件重新渲染,但其实这是不必要,这时就可以采用useCallback,将函数handleSubmit传递给useCallback,并设置依赖项([A,B]),保证只有依赖项(A,B)发生变化时,才会重新生成handleSubmit函数,子组件才会重新渲染,从而避免了组件内部重新创建函数,避免引起不必要的渲染。

  • useMemo
    • 避免无效的重新计算
    • 跳过组件的重新渲染

同理,比如说父组件中接收的props有{A,B,C},有一个对象count是依赖于A和B计算得出的,并将这个对象count作为子组件的props,正常情况当C发生变化,会导致重新创建对象count,导致传递给子组件的props不同,从而引起子组件的重新渲染,但这是不必要的重复渲染,这时就可以采用useMemo,将定义对象count的计算函数用useMemo包裹,并设置依赖项[A,B],这样就可以保证只有在依赖项发生变化时,才会重新执行计算函数生成新的count对象,子组件才会重新渲染,从而避免了组件内部重新进行无效计算,避免引起不必要的渲染。

useContext

用于跨组件通信,以便共享数据和功能。它接收一个上下文对象(通过 React.createContext 创建)并返回当前上下文的值。这样,函数组件就可以消费上下文中的数据,而不必通过一层层的属性传递。

import { createContext, useContext } from 'react';
const ThemeContext = createContext(null);export default function MyApp() {return (<ThemeContext.Provider value="dark"><Form /></ThemeContext.Provider>)
}function Form() {return (<Panel title="Welcome"><Button>Sign up</Button><Button>Log in</Button></Panel>);
}function Panel({ title, children }) {const theme = useContext(ThemeContext);const className = 'panel-' + theme;return (<section className={className}><h1>{title}</h1>{children}</section>)
}function Button({ children }) {const theme = useContext(ThemeContext);const className = 'button-' + theme;return (<button className={className}>{children}</button>);
}

useContext() 总是在调用它的组件 上面 寻找最近的 provider。它向上搜索, 不考虑 调用 useContext() 的组件中的 provider。

useRef

const ref = useRef(initialValue)
  • 用于在函数组件之间保存可变的引用,类似于类组件中的 ref。改变 ref 不会触发重新渲染。

  • 使用 ref 操作 DOM

    import { useRef } from 'react';
    export default function Form() {const inputRef = useRef(null);function handleClick() {inputRef.current.focus();}return (<><input ref={inputRef} /><MyInput ref={inputRef} />;// 无法获取自定义组件的 ref <button onClick={handleClick}>聚焦输入框</button></>);
    }
    
  • 无法获取自定义组件的 ref
    默认情况下,自定义组件不会暴露它们内部 DOM 节点的 ref。
    像这样将自定义子组件包装在 forwardRef 里,父级组件就可以得到它的 ref。

    import { forwardRef } from 'react';
    const MyInput = forwardRef(({ value, onChange }, ref) => 		{return (<inputvalue={value}onChange={onChange}ref={ref}/>);
    });
    export default MyInput;
    

3. 函数组件与类组件的区别

3.1 表面差异

  • 类组件有生命周期,函数组件没有
  • 类组件需要继承 Class,函数组件不需要
  • 类组件可以获取实例化的 this,并且基于 this 做各种操作,函数组件不行
  • 类组件内部可以定义并维护 state, 函数组件为无状态组件(可以通过hooks实现)

函数组件相比较类组件,优点是更轻量与灵活,便于逻辑的拆分复用。

3.2 最大不同

类组件捕获最新的值,函数式组件捕获了渲染时所使用的值 , 这是两类组件最大的不同。

在React中,函数组件 每次渲染组件中的变量和事件处理函数就会重新被创建。每次渲染React都会提供给这次渲染的state快照,也就是说state 的值始终“固定”在一次渲染的各个事件处理函数内部。所以,过去创建的事件处理函数拥有的是创建它们的那次渲染中的 state 值。
而在类组件中,事件处理函数是通过this获取最新的state值。

下面这个发送信息的例子是官网上的例子,我们分别在类组件和函数组件中测试一下看看有什么不同:
在这里插入图片描述
类组件中:

class Form1 extends React.Component{state={to:'Alice',message:'Hello'}handleSubmit=(e)=>{e.preventDefault();setTimeout(() => {alert(`You said ${this.state.message} to ${this.state.to}`);}, 5000);}render(){return  <form onSubmit={this.handleSubmit}><label>To:{' '}<selectvalue={this.state.to}onChange={e => this.setState({to:e.target.value})}><option value="Alice">Alice</option><option value="Bob">Bob</option></select></label><textareaplaceholder="Message"value={this.state.message}onChange={e => this.setState({message:e.target.value})}/><button type="submit">Send</button></form>}
}

函数组件中:

import { useState } from 'react';
export default function Form() {const [to, setTo] = useState('Alice');const [message, setMessage] = useState('Hello');function handleSubmit(e) {e.preventDefault();setTimeout(() => {alert(`You said ${message} to ${to}`);}, 5000);}return (<form onSubmit={handleSubmit}><label>To:{' '}<selectvalue={to}onChange={e => setTo(e.target.value)}><option value="Alice">Alice</option><option value="Bob">Bob</option></select></label><textareaplaceholder="Message"value={message}onChange={e => setMessage(e.target.value)}/><button type="submit">Send</button></form>);
}

仔细对比,会发现:

  • 类组件中,当点击【Send】按钮后,如果在5秒内修改收件人(下拉列表中的选项,由Alice—>Bob),那么最终页面alert弹窗内容中收件人是最新修改的名字Bob。
  • 函数组件中,重复以上操作,5秒后,最终页面alert弹窗内容中收件人是点击【Send】按钮时的名字Alice,不是最新修改的名字Bob。

此案例中可以说明,在函数组件中,一个 state 变量的值永远不会在一次渲染的内部发生变化即使其事件处理函数的代码是异步的。React会为每次特定的那一次渲染提供一张 state 快照。组件会在其 JSX 中返回一张包含一整套新的 props 和事件处理函数的 UI 快照 ,其中所有的值都是 根据那一次渲染中 state 的值 被计算出来的!

原因

  • 类组件: 可以直接获取组件最新的值

    原因:类组件捕获最新的值(永远保持一致)当实例的state或props属性发生修改时,类组件直接使用this(组件的实例),所以可以直接获取组件最新的值

  • 函数组件: 函数式组件捕获了渲染时所用的值

    原因:函数式组件捕获了渲染所使用的值。在函数组件中,之前的state或props值,已经因为javaScript闭包的特性,保存在内存之中,无法从外部进行修改(维护多个人的状态)。 所以在定时器执行callback时,打印的还是旧值。

下面是在javaScript闭包中的测试

<body><button onclick="openClick()">开始</button>  <button onclick="handleClick()">改变a的值</button>    
</body>
<script>
function parent(arg){let a=arg;return function children(){alert('a的值是:'+ a);}
}function openClick(){setTimeout(()=>{parent(111)();},3000);
}function handleClick(){parent(222);
}
</script>

当点击【开始】按钮时,会使用定时器在三秒后调用闭包函数,页面弹出a的值111,如果在这三秒内点击了【改变a的值】按钮,触发事件修改闭包的外层函数的作用域下的a的值,但到3秒时页面弹出a的值仍然是111。

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

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

相关文章

面试八股文之JAVA基础

JAVA基础 DNS、CDN&#xff1f;如何实现对象克隆?父子类静态代码块, 非静态代码块, 构造方法执行顺序?String s new String("abc") 创建了几个对象, 分别放到哪里?OSI网络模型七层&#xff1f;应用层协议&#xff1f;http协议和https协议区别&#xff1f;传输层协…

蓝桥杯小白月赛3.23

题目描述&#xff1a; AC代码&#xff1a; #include <iostream> #include<cstring> #include<algorithm>using namespace std;const int N 2e510; string str[N]; //写上&会速度更快一些 bool cmp(const string &s1,const string &s2) {//例…

苹果App Store上架工具介绍

文章目录 摘要引言正文1. Xcode2. [appuploder](https://www.applicationloader.net/)3. [克魔助手](https://keymob.com/) 4.[ipa guard](https://www.ipaguard.com/)总结参考资料 摘要 苹果App Store作为iOS应用程序的主要分发渠道&#xff0c;上架应用程序需要遵守规定和通…

华为数通方向HCIP-DataCom H12-821题库(多选题:201-220)

第201题 以下关于BGP中Orginator ID属性的描述,正确的是哪些项? A、Originator ID属于公认任意属性 B、当其他BGP Speaker接收到这条路由的时候,将比较收到的0nginator ID和本地的Router ID,如果两个ID相同BGP Speaker会忽略掉这条路由,不做处理 C、当一条路由第一次被RR…

输入与输出

输入(Scanner类) Scanner是java5的新特性&#xff0c;在java.util包里&#xff0c;可以完成用户输入。步骤&#xff1a; 导入java.util包&#xff1b;构造Scanner对象&#xff0c;参数为u标准输入流System.in&#xff1b;使用next()方法系列接收数据 nextBoolean()接收一个布…

后端基础篇- 社区 IDEA 手动 Maven 创建 SpringBoot 项目、Maven 安装与配置环境变量、IDEA 集成 Maven

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Maven 安装与配置环境变量 1.1 下载并解压安装包 1.2 配置本地仓库 1.3 配置阿里云私服 1.4 配置环境变量 2.0 IDEA 集成 Maven 2.1 首先创建一个新项目 2.2 开始…

电商系列之满减

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

【4月】组队打卡《山有木Python特征工程极简入门》

活动名称 CDA Club 第2期《山有木兮Python数据分析极简入门》组队打卡 活动介绍 本次打卡活动由CDA俱乐部旗下学术部主办。目的是通过数据分析科普内容&#xff0c;为数据分析爱好者提供学习和交流的机会。方便大家利用碎片化时间在线学习&#xff0c;以组队打卡的形式提升学…

一文读懂:什么是工单系统?市面上有哪些好用的工单系统?

什么是工单管理系统&#xff1f;工单系统如何帮助企业解决管理问题&#xff1f;市面上有哪些好用的工单管理系统&#xff1f;不同工单管理系统适用于什么企业&#xff1f;工单管理系统如何定价&#xff1f; 5000字长文&#xff0c;我写了整整一天&#xff01;梳理了大家对工单…

framebuffer

framebuffer 1.framebuffer的理解 概念&#xff1a;FrameBuffer&#xff0c;可以译作"帧缓冲"&#xff0c;有时简称为 fbdrv&#xff0c;这是一种独立于硬件的抽象图形设备&#xff0c;是Linux为显示设备提供的一个接口&#xff0c;把显存抽象后的一种设备&#xf…

GitHub学生认证

文件、证明之类的一定要用英文。 我先是用有道网页翻译把学信网的报告翻译成了英文&#xff0c;然后截图传上去&#xff0c; 给我这个答复 所以要先2FA认证、支付信息填好。 2FA认证&#xff1a;Github开启2FA双重验证 - 知乎 (zhihu.com) 支付信息&#xff1a;点击Setting…

【学习】软件测试行业 ,有哪些以就业为主的学习侧重点

今天给所有入行软测的同学们&#xff0c;帮大家梳理下以就业为主的学习侧重点&#xff0c;简单来说就是【这些都是重点&#xff0c;圈起来&#xff0c;要考的】&#xff0c;有需要的小伙伴可以往下看。 建议一&#xff1a;一定要学习一门编程语言&#xff0c;再开始使用自动化测…

LC 100.相同的树

100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a; p [1,2,3], q [1,2,3] 输出&#xff1…

PostgreSQL技术大讲堂 - 第48讲:PG高可用实现keepalived

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第48讲&#…

IPv6-基础概念

IPv6基础概念 IPv6技术特点&#xff1a;精简报文格式、实现自动配置和重新编制、支持层次化网络编制、支持端对端安全、更好的支持Qos、支持移动特性。 五元组&#xff1a;源地址&#xff0c;目的地址&#xff0c;源端口&#xff0c;目的端口&#xff0c;协议。 IPv6报头优势…

gitee 本地文件提交到仓库

一、准备工作 1.下载Git Bash Git Bash官网下载地址 http://www.git-scm.com/download/ 点此跳转 2.注册或登录gitee gitee官网地址 https://gitee.com/ 点此跳转 没有账号选择注册有账号的话直接登陆 3.在gitee中新建一个空的仓库 登陆成功后点进个人主页&#xff0c;点击…

美国免税州详细地址:可用于ChatGPT、MJ、POE、AppStore等订阅服务账单地址填写!

地址1zip_Codecountriesstatecitystreet199654USAAKWasilla2075 E Chickaloon Rd236110USAAL MontgomeryMontgomery372034USAARConway617 Watkins St486303USAAZPrescott330 S Cortez St592805USACAAnaheim505 S Zeyn St680232USACOLakewood1390 S Zephyr St706109USACTWether…

Pygame基础4-多阶段

4-多阶段 原理 本章是对第2章的延续。 我们希望为游戏添加多个阶段&#xff0c;比如开始界面、游戏界面、结束界面等。 为此&#xff0c;我们可以使用一个类来管理游戏的不同阶段。 我们创建了GameManager类&#xff0c;并通过self.status属性表示不同的阶段。 intro阶段…

javaSSM公司招聘管理系统IDEA开发mysql数据库web结构计算机java编程maven项目

一、源码特点 IDEA开发SSM公司招聘管理系统是一套完善的完整企业内部系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;MAVEN方式加 载&#xff0c;系统具有完整的源代码和…

solr-lucene的解释

什么是solr-lucene Solr是一个基于Apache Lucene的开源搜索平台。Lucene是一个开源的全文搜索引擎库&#xff0c;它提供了一个简单而强大的API&#xff0c;用于索引和搜索文档。Solr通过在Lucene的基础上构建一个更高级的搜索平台&#xff0c;为用户提供了更丰富的功能和易于使…