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 {
  // 初始化类组件的 state
  state = {
    text: "111"
  };
  // 编写生命周期方法 didMount
  componentDidMount() {
    // 省略业务逻辑
  }
  // 编写自定义的实例方法
  changeText = (newText) => {
    // 更新 state
    this.setState({
      text: newText
    });
  };
  // 编写生命周期方法 render
  render() {
    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 } = props
  return (
    <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 (
        <input
          value={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>
          <select
            value={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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用Unidbg实现阿里系x-sign签名, 成功实现长x-mini-wua

本篇文章仅供学习讨论。 文章中涉及到的代码、实例&#xff0c;仅是个人日常学习研究的部分成果。 如有不当&#xff0c;请联系删除。 阿里系的签名算法&#xff0c;一直让人好奇的心痒痒。所以在空的时候&#xff0c;都会去扣其逻辑&#xff0c;一边学习逆向&#xff0c;一边学…

jmeter之接口功能自动化

一、接口测试简述 接口&#xff1a;用来连接前端&#xff0c;后端还有移动端的程序模块。由于不同端的工作进度不一样&#xff0c;需要对最开始出来的接口进行接口测试。 接口分类&#xff1a;POST&#xff0c;GET&#xff0c;PUT&#xff0c;DELETE。 POST请求的数据是放在…

极简自建web视频会议,私有云,rtmp/rtsp/webrtc一键参会直播会议互动方案

随着视频互动深入工作日常&#xff0c;很多客户需要自建一个会议&#xff0c;监控的交互平台&#xff0c;目前外面不管是开源还是非开源的平台&#xff0c;都是极为复杂&#xff0c;一般linux安装库关联部署复杂&#xff0c;非技术人员根本没办法使用&#xff0c;不方便集成部署…

基于springboot+vue的宠物商城网站

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

AAC相关知识

一、AAC音频格式种类有哪些 AAC音频格式是一种由MPEG-4标准定义的有损音频压缩格式。AAC包含两种封装格式 ADIF&#xff08;Audio Data Interchange Format音频数据交换格式&#xff09;和ADTS&#xff08;Audio Data transport Stream音频数据传输流&#xff09;。 ADIF 特点…

38 mars3d 对接地图图层 绘制点线面员

前言 这里主要是展示一下 mars3d 的一个基础的使用 主要是设计 接入地图服务器的 卫星地图, 普通的二维地图, 增加地区标记 基础绘制 点线面园 等等 测试用例 <template><div style"width: 1920px; height:1080px;"><div class"mars3dClas…

阿里云原生:如何熟悉一个系统

原文地址:https://mp.weixin.qq.com/s/J8eK-qRMkmHEQZ_dVts9aQ?poc_tokenHMA-_mWjfcDmGVW6hXX1xEDDvuJPE3pL9-8uSlyY 导读&#xff1a;本文总结了熟悉系统主要分三部分&#xff1a;业务学习、技术学习、实战。每部分会梳理一些在学习过程中需要解答的问题&#xff0c;这些问题…

Matlab|基于条件风险价值CVaR的微网动态定价与调度策略

目录 1 主要内容 模型示意图 电能交易流程 模型亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序复现文章《A cooperative Stackelberg game based energy management considering price discrimination and risk assessment》&#xff0c;建立基于主从博弈的考虑…

神级工具之git (一): git 基操

一切都从&#xff1a;Git User Manual开始&#xff0c;或者中文版的Git中文手册 核心概念 工作区 工作区我们可见的&#xff0c;可以进行修改的目录树。我们可以在目录树中进行文件的查看&#xff0c;修改。通常我们会使用一个神级编辑器Vim。我给她取了个名字&#xff0c;就…

第十一届蓝桥杯大赛第二场省赛试题 CC++ 研究生组-子串分值和

solution1&#xff08;通过40%&#xff09; 依次求子串并统计出现过的字母个数 #include<iostream> #include<string> #include<set> using namespace std; int main(){string s, subs;cin >> s;int len s.size(), ans 0;for(int j 1; j < len…

Oracle Data Guard部署

Oracle的主备DG搭建 1. 修改主机名,同步时间 主库IP&#xff1a;192.168.100.137 备库IP&#xff1a;192.168.100.138配置主机名(主库) Hostname zygjpdb vim /etc/hosts 192.168.100.137 zygjpdb 192.168.100.138 zygjsdbvim /etc/sysconfig/network HOSTNAMEzygjpdb ------…

MySQL 排序的那些事儿

书接上回 上次发了几张图&#xff0c;给了几个MySQL Explain的场景&#xff0c;链接在这儿&#xff1a;你是不是MySQL老司机&#xff1f;来看看这些explain结果你能解释吗&#xff1f;MySQL 夺命6连问 我们依次来分析下这6个问题。 在分析之前&#xff0c;我们先来了解一下M…

(附源码)基于Spring Boot和Vue的智能订餐与外卖系统设计与实现

1. 引言 这部分通常包含了研究背景、研究意义、国内外研究现状、本文研究内容以及论文结构安排。 研究背景&#xff1a;介绍当前外卖市场的快速发展&#xff0c;以及智能订餐系统对改善人们生活的影响。研究意义&#xff1a;强调这类系统在现代生活中的作用和开发的创新点。国…

【JavaEE初阶系列】——带你了解volatile关键字以及wait()和notify()两方法背后的原理

目录 &#x1f6a9;volatile关键字 &#x1f388;volatile 不保证原子性 &#x1f388;synchronized 也能保证内存可见性 &#x1f388;Volatile与Synchronized比较 &#x1f6a9;wait和notify &#x1f388;wait()方法 &#x1f4bb;wait(参数)方法 &#x1f388;noti…

关于JAVA8的Lambda表达式

1. 水在前面 这个礼拜忽然心血来潮把Lambda表达式学习了一遍&#xff0c;发现这玩意跟原来想象的好像不是一个东西&#xff0c;写个学习心得供以后复习用。还是那句话&#xff0c;这篇水文不能让你完全掌握&#xff0c;只是用来给我自己温习用的&#xff0c;或者也可以作为小伙…

jmeter使用方法---自动化测试

HTTP信息头管理器 一个http请求会发送请求到服务器&#xff0c;请求里面包含&#xff1a;请求头、请求正文、请求体&#xff0c;请求头就是信息头Authorization头的主要用作http协议的认证。 Authorization的作用是当客户端访问受口令保护时&#xff0c;服务器端会发送401状态…

JMeter并发工具的使用

视频地址&#xff1a;Jmeter安装教程01_Jmeter之安装以及环境变量配置_哔哩哔哩_bilibili 一、JMeter是什么 JMeter是一款免安装包&#xff0c;官网下载好后直接解压缩并配置好环境变量就可以使用。 环境变量配置可参考&#xff1a;https://www.cnblogs.com/liulinghua90/p/…

阿里云效流水线—发布公用jar到Maven私仓

后端项目发布 1.选择流水线 2.新建流水线 3.选择模板 4.选择代码仓库 5.调整构建命令 添加mvn install 重新构建项目 6.添加镜像 在wms-app目录下新建Dockerfile文件(Dockerfile文件名中的D一定要是大写的&#xff09;文件&#xff0c;重新推送项目 #基础镜像 FROM openjd…

windows libcurl异常排查 杀毒与防火墙拦截

今天遇到一个机器&#xff0c; libcurl库访问报错&#xff0c;6 解析主机异常 后来下载了一个curl客户端放到机器上&#xff0c;访问报 curl getaddrinfo thread failed to start查找一些资料&#xff0c;说是杀毒软件对网络做了限制 后来通过允许程序通过网络防火墙解决此问…

C# WPF编程-控件

C# WPF编程-控件 概述WPF控件类别包括以下控件&#xff1a;背景画刷和前景画刷字体文本装饰和排版字体继承字体替换字体嵌入文本格式化模式鼠标光标 内容控件Label&#xff08;标签&#xff09;Button&#xff08;按钮&#xff09; 概述 在WPF领域&#xff0c;控件通常被描述为…