当前位置: 首页 > news >正文

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);},[]);
//3. 如果设置依赖项且不为空,那么在组件挂载时和数组中的依赖项发生变化时,副作用就会重新执行。useEffect(() => {console.log("useEffect副作用执行");setTimeout(() => {setList(result);}, 3000);}, [list]);

useEffect用于处理组件的副作用

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

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 最大不同

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

import React from "react";
import ReactDOM from "react-dom";import ProfilePageFunction from './ProfilePageFunction';
import ProfilePageClass from './ProfilePageClass';class App extends React.Component {state = {user: 'Dan',};render() {return (<><label><b>Choose profile to view: </b><selectvalue={this.state.user}onChange={e => this.setState({ user: e.target.value })}><option value="Dan">Dan</option><option value="Sophie">Sophie</option><option value="Sunil">Sunil</option></select></label><h1>Welcome to {this.state.user}’s profile!</h1><p>//函数组件<ProfilePageFunction user={this.state.user} /><b> (function)</b></p><p>//类组件<ProfilePageClass user={this.state.user} /><b> (class)</b></p><p>Can you spot the difference in the behavior?</p></>)}
}const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

类组件 —— ProfilePageClass.js

import React from 'react';class ProfilePage extends React.Component {showMessage = () => {alert('Followed ' + this.props.user);};handleClick = () => {setTimeout(this.showMessage, 3000);};render() {return <button onClick={this.handleClick}>Follow</button>;}
}
export default ProfilePage;

函数组件 —— ProfilePageFunction.js

import React from 'react';function ProfilePage(props) {const showMessage = () => {alert('Followed ' + props.user);};const handleClick = () => {setTimeout(showMessage, 3000);};return (<button onClick={handleClick}>Follow</button>);
}
export default ProfilePage;

仔细对比,以上代码的不同
在这里插入图片描述
函数组件的测试情况下,下拉框中选中 Dan,点击关注按钮,迅速将下拉框切换到Sophie,3秒之后,alert弹窗内容仍然是 Followed Dan

类组件的测试情况下,重复相同的动作,3秒之后,alert弹窗将会显示 Followed Sophie

此案例中,当父组件的state改变时,子组件进行重新渲染,子组件的props改变;

原因

  • 类组件:
    props改变(Dan->Sophie),类组件也改变了值,永远保持一致。

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

  • 函数组件: 函数式组件捕获了渲染时所用的值
    当改变props时(Dan->Sophie),你会发现函数组件会渲染之前的值Dan这个名字。

    原因:函数式组件捕获了渲染所使用的值。在函数组件中,之前的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。

相关文章:

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

类组件 与 函数组件 的区别 1. 类组件2. 函数组件HookuseStateuseEffectuseCallbackuseMemouseContextuseRef 3. 函数组件与类组件的区别3.1 表面差异3.2 最大不同原因 1. 类组件 在React中&#xff0c;类组件就是基于ES6语法&#xff0c;通过继承 React.component 得到的组件…...

GPT实战系列-智谱GLM-4的模型调用

GPT实战系列-智谱GLM-4的模型调用 GPT专栏文章&#xff1a; GPT实战系列-实战Qwen通义千问在Cuda 1224G部署方案_通义千问 ptuning-CSDN博客 GPT实战系列-ChatGLM3本地部署CUDA111080Ti显卡24G实战方案 GPT实战系列-Baichuan2本地化部署实战方案 GPT实战系列-让CodeGeeX2帮…...

AndroidStudio开发 相关依赖

1、com.google.zxing 用于二维码扫描 2、butterknife 用于简化findView 和 onClick操作 3、pub.devrel:easypermissions 简化权限请求的库 4、 网络请求框架(一)&#xff1a;android-async-http 网络请求框架(二)&#xff1a;xUtils 网络请求框架(三)&#xff1a;Volley Volley…...

Zookeeper详解(zk)

文章目录 Zookeeper 概念ZooKeeper的应用场景使用场景zk的原理ZooKeeper、Nacos、Eureka 和 Consul区别Zookeeper的数据结构zk集群脑裂如何解决ZAB 协议假如注册中心挂了&#xff0c;消费者还能调⽤服务吗&#xff0c;用什么调用的dubbo注册中心为什么选择 Zookeeper关于zookee…...

BSD-3-Clause是一种开源软件许可协议

BSD-3-Clause是一种开源软件许可协议&#xff0c;也称为BSD三条款许可证。它是BSD许可证家族中的一种&#xff0c;是一种宽松的许可证&#xff0c;允许软件自由使用、修改和重新分发&#xff0c;同时也保留了一些版权和责任方面的规定。 BSD-3-Clause许可证的主要特点包括以下…...

持续集成平台 02 jenkins plugin 插件

拓展阅读 Devops-01-devops 是什么&#xff1f; Devops-02-Jpom 简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件 代码质量管理 SonarQube-01-入门介绍 项目管理平台-01-jira 入门介绍 缺陷跟踪管理系统&#xff0c;为针对缺陷管理、任务追踪和项目管理的商业…...

LoadBalancerCacheManager not available, returning delegate without caching

警告&#xff1a;LoadBalancerCacheManager not available, returning delegate without caching 背景&#xff1a;更换了redis集群 解决方案&#xff1a; 重启gateway网关服务 也就是重启引用下面这个包的服务 <dependency><groupId>org.springframework.cloud…...

机器学习金融应用技术指南

1 范围 本文件提供了金融业开展机器学习应用涉及的体系框架、计算资源、数据资源、机器学习引擎、机 器学习服务、安全管理、内控管理等方面的建议。 本文件适用于开展机器学习金融应用的金融机构、技术服务商、第三方安全评估机构等。 2 规范性引用文件 下列文件中的内容通过…...

ES6生成器(Generator)

一、function* 概念简介&#xff1a;function* - JavaScript | MDN (mozilla.org) function* 声明创建一个绑定到给定名称的新生成器函数。生成器函数可以退出&#xff0c;并在稍后重新进入&#xff0c;其上下文&#xff08;变量绑定&#xff09;会在重新进入时保存。 1.1 y…...

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 并训练自己的数据集

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 概述 大模型微调(finetuning)以适应特定任务是一个复杂且计算密集型的过程。本文训练测试主要是基于主流的的微调方法:LoRA、Adapter、Prefix-tuning、P-tuning和Prompt-tuning,并对…...

【No.13】蓝桥杯二分查找|整数二分|实数二分|跳石头|M次方根|分巧克力(C++)

二分查找算法 知识点 二分查找原理讲解在单调递增序列 a 中查找 x 或 x 的后继在单调递增序列 a 中查找 x 或 x 的前驱 二分查找算法讲解 枚举查找即顺序查找&#xff0c; 实现原理是逐个比较数组 a[0:n-1] 中的元素&#xff0c;直到找到元素 x 或搜索整个数组后确定 x 不在…...

【蓝桥杯-单片机】基于定时器的倒计时程序设计

基于定时器的倒计时程序 题目如下所示&#xff1a; 实现过程中遇到的一些问题 01 如何改变Seg_Buf数组的值数码管总是一致地显示0 1 2 3 4 5 首先这个问题不是在main.c中关于数码管显示部分的逻辑错误&#xff0c;就是发生在数码管的底层错误。 检查了逻辑部分&#xff…...

QT:三大特性

QT的三大特性&#xff1a; 1、信号与槽 2、内存管理 3、事件处理 1、信号与槽 当信号产生时&#xff0c;就会自动调用绑定的槽函数。 自定义信号: 类中需要添加O_OBJECT宏 声明: signals标签之下进行声明 定义&#xff1a; 信号不需要定义 …...

无服务器推理在大语言模型中的未来

服务器无服务器推理的未来&#xff1a;大型语言模型 摘要 随着大型语言模型(LLM)如GPT-4和PaLM的进步&#xff0c;自然语言任务的能力得到了显著提升。LLM被广泛应用于聊天机器人、搜索引擎和编程助手等场景。然而&#xff0c;由于LLM对GPU和内存的巨大需求&#xff0c;其在规…...

【linux】CentOS查看系统信息

一、查看版本号 在CentOS中&#xff0c;可以通过多种方法来查看版本号。以下是几种常用的方法&#xff1a; 使用cat命令查看/etc/centos-release文件&#xff1a; CentOS的版本信息存储在/etc/centos-release文件中。可以使用cat命令来显示该文件的内容&#xff0c;从而获得C…...

部署dagu_1.12.10+replicadb0.15.1+sqlline1.12

下载所需的文件并上传到服务器 dagu_1.12.10(选择适合自己的版本): https://gh.api.99988866.xyz/https://github.com/dagu-dev/dagu/releases/download/v1.12.10/dagu_1.12.10_linux_amd64.tar.gzreplicadb0.15.1(包含了sqlline1.12): https://download.csdn.net/download/u0…...

基于Java中的SSM框架实现社会保险管理系统项目【项目源码+论文说明】

摘要 改革开放的浪潮已近深深的键入人心&#xff0c;随之而带来了我国社会主义市场经济体制的不断完善和建立健全&#xff0c;作为国家基本保障之一的社会保险系统&#xff0c;我们不单单要把握社保金额的进展和状态&#xff0c;还需要对社会保障基金开展全面、科学、可持续的…...

24计算机考研调剂 | 江西理工大学

能源、化工、计算机&#xff08;0854&#xff09;等相关方向有3个调剂名额 考研调剂招生信息 学校:江西理工大学 专业:工学->治金工程 年级:2024 招生人数:3 招生状态:正在招生中 联系方式:********* (为保护个人隐私,联系方式仅限APP查看) 补充内容 能源、化工、计…...

Sentry(Android)源码解析

本文字数&#xff1a;16030字 预计阅读时间&#xff1a;40分钟 01 前言 Sentry是一个日志记录、错误上报、性能监控的开源框架&#xff0c;支持众多平台&#xff1a; 其使用方式在本文不进行说明了&#xff0c;大家可参照官方文档&#xff1a;https://docs.sentry.io/platforms…...

Android StateLayout状态页

文章目录 Android StateLayout状态页概述源码使用源码下载 Android StateLayout状态页 概述 StateLayout&#xff08;状态页&#xff09;包含&#xff1a;加载中页面&#xff0c;错误页面&#xff0c;空页面&#xff0c;内含状态默认页面&#xff0c;支持自定义页面。 源码 …...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...