React 系列 之 React Hooks(一) JSX本质、理解Hooks

news2024/11/14 20:27:32

借鉴自极客时间《React Hooks 核心原理与实战》

JSX语法的本质

可以认为JSX是一种语法糖,允许将html和js代码进行结合。
JSX文件会通过babel编译成js文件
下面有一段JSX代码,实现了一个Counter组件

import React from "react";

export default function Counter() {
  const [count, setCount] = React.useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </div>
  );
}

这段代码也可以形成js:

React.createElement(
  "div",
  null,
  React.createElement(
    "button",
    { onClick: function onClick() {
        return setCount(count + 1);
      } },
    React.createElement(CountLabel, { count: count })
  )
);

用到了React的一个api:React.createElement,它的作用是创建组件的实例。这个api的参数:

  • 第一个参数:组件类型,内置组件(对应于html元素)为小写,自定义组件为大写驼峰
  • 第二个参数:为组件添加的属性,即props
  • 第三个及以后的参数:该组件的children

所以呢,通过 createElement 这个 API,我们可以构建出需要的组件树,而 JSX 只是让这种描述变得更加直观和高效。
因此,JSX 不是一个新的概念,而只是原生 JavaScript 的另一种写法。但是换成这种写法,会大大降低你上手 React 的难度。

React组件的本质

从model(state+props)到 view的映射
在这里插入图片描述
React 会帮助你处理所有 DOM 变化的细节。而且,当 Model 中的状态发生变化时,UI 会自动变化,即所谓的数据绑定。
把UI的展现看做是函数的执行

f(变化的model)  => 新的dom树,然后把新的dom树以最优的方法更新到浏览器

为什么要发明 Hooks?

Class组件,有不适合的地方,浪费了类的两个特点:1. 不需要人为调用Class实例的方法,这个Class的特点就浪费了 2. React的类之间没有继承,这个特点也浪费了。
函数组件,也有不合适的地方,1. 没有自己的生命周期,2. 函数自身无法记录状态

我们想要强化函数组件,让它能有状态,让它关联到一个存储在外部的状态,那么Hooks的思想就诞生了

把一个外部的数据绑定到(钩到)函数的执行。
当被钩到的数据或事件发生变化时,产生这个目标结果的代码会重新执行,产生更新后的结果。
对于函数组件,这个结果是最终的 DOM 树

有一点需要特别注意,Hooks 中被钩的对象,不仅可以是某个独立的数据源,也可以是另一个 Hook 执行的结果,这就带来了 Hooks 的最大好处:逻辑的复用。

hooks的过人之处

1 逻辑复用

或者说是简化了逻辑复用
以往的一些逻辑复用,需要借助高阶组件来完成(这是一个复杂的设计模式),但是hooks就能简化这一过程。
举个例子: 调整窗口大小
a. 高阶组件方法
高阶组件实现思路:高阶组件负责监听窗口大小变化,并将变化后的值作为 props 传给下一个组件。

// 组件为参数,最后返回一个包装过的组件
const withWindowSize = Component => {
	// 产生一个高阶组件 WrappedComponent,只包含监听窗口大小的逻辑
	class WrappedComponent extends React.PureComponent {
		constructor(props) {
			super(props);
			this.state = {
			  size: this.getSize() 
			};
		}
		// 组件生命周期对应状态 挂到window的listener上
		componentDidMount() {
		    window.addEventListener("resize", this.handleResize);
		}
		componentWillUnmount() {
			window.removeEventListener("resize", this.handleResize);
		}
		// 获取当前window的size
		getSize() {
			return window.innerWidth > 1000 ? "large" : "small";
		}
		handleResize = () => {
			const currentSize = this.getSize();
			this.setState({
				size: this.getSize()
			});
		}
		render() {
		    // 将窗口大小传递给真正的业务逻辑组件
			return <Component size={this.state.size} />
		}
	}
	return WrappedComponent;
};

然后在自定义组件中调用withWindowSize 这样的函数来产生一个新组件,并自带size属性,例如:

class MyComponent extends React.Component {
	render() {
		const {size} = this.props;
		if(size === 'small') return <SmallComponent />;
		else return <LargeComponent />;
	}
}
// 使用 withWindowSize 产生高阶组件,用于产生 size 属性传递给真正的业务组件
export default withWindowSize(MyComponent)

因为size是props中得到的,即是从父组件传递下来的,所以当WrappedComponent组件中监听到窗口变化时,会更新size的值,从而会让MyComponent重新渲染

高阶组件的缺点:

  1. 代码难理解,不直观,很多人甚至宁愿重复代码,也不愿用高阶组件;
  2. 增加额外的组件节点。每一个高阶组件都会多一层节点,这就会给调试带来很大的负担。

那么用hooks如何实现?
b. hooks方法


const useWindowSize = () => {
  const [size, setSize] = useState(getSize());
  useEffect(() => {
    const handler = () => { setSize(getSize()); }
    window.addEventListener('resize', handler);
    // 回调函数可以返回一个清理函数。这个清理函数在组件卸载时或者在下一次 effect 执行之前执行,
    return () => {
      window.removeEventListener('resize', handler);
    };
  }, []);  // 空的依赖项数组意味着此效果只会运行一次,类似于类组件中的 componentDidMount
  return size;
}

在组件中使用useWindowSize

const Demo = () => {
  const size = useWindowSize();
  if (size == 'small') return <SmallComponent />;
  else return <LargeComponent />;
}

窗口大小是一个外部的数据状态,但我们通过Hooks的方式对其进行了封装,将其变成一个可绑定的数据源。每当窗口大小发生变化时,使用这个Hook的组件就会重新渲染。(当窗口大小改变时,注册的事件监听器会触发,并调用 handler 函数,该函数会更新 size 状态的值。在 Demo 组件中,根据 size 的值来决定渲染 SmallComponent 还是 LargeComponent。当 size 的值在窗口大小变化时被更新后,会触发组件的重新渲染,因为组件的渲染取决于 size 的值)

2 有助于关注分离

Hooks能够将针对同一个业务逻辑的代码尽可能聚合在一块儿,让代码更容易理解和维护,相比之下,Class组件无法做到这一点,因为Class组件中,不得不把同一个业务逻辑的代码分散在类组件的不同生命周期方法中。
举个例子:上面的窗口大小变化监听代码
Class组件中,我们在componentDidMountcomponentWillUnmount中分别去监听事件和解绑事件;
而在函数组件中,就可以把逻辑都集中写在hooks里

Hooks所解决的问题是什么?

  1. 更好地体现了React的开发思想,即 State => View 的函数式映射
  2. 更好地解决了 Class 组件存在的一些代码冗余、难以逻辑复用的问题

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

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

相关文章

【机器学习】深入解析线性回归模型

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

原创!分解+集成思想新模型!VMD-CNN-BiGRU-Attention一键实现时间序列预测!以风速数据集为例

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 数据介绍 模型流程 创新点 结果展示 部…

【系统架构设计师】计算机系统基础知识 03

系统架构设计师 - 系列文章目录 01 系统工程与信息系统基础 02 软件架构设计 03 计算机系统基础知识 文章目录 系统架构设计师 - 系列文章目录 文章目录 前言 一、计算机系统概述 1.计算机组成 ​编辑2.存储系统 二、操作系统 ★★★★ 1.进程管理 2.存储管理 1.页式存储 …

Java:设计模式

文章目录 参考简介工厂模式简单工厂模式工厂方法模式抽象工厂模式总结 单例模式预加载懒加载线程安全问题 策略模式 参考 知乎 简介 总体来说设计模式分为三类共23种。 创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模…

Java 22正式发布,一文了解全部新特性

就在昨晚&#xff0c;Java 22正式发布&#xff01;该版本提供了 12 项功能增强&#xff0c;其中包括 7 项预览功能和 1 项孵化器功能。它们涵盖了对 Java 语言、API、性能以及 JDK 中包含的工具的改进。 下面就来一起学习一下该版本都更新了哪些新特性&#xff01; Unnamed V…

世媒讯软文营销策略如何做才能达到引流的目的

软文营销是一种通过撰写软文来宣传企业、产品或服务的网络营销方式。通过撰写具有故事性、吸引人的文章来间接推广产品、服务或品牌的营销策略。要实现软文营销的目的&#xff0c;即引流&#xff08;吸引流量&#xff09;&#xff0c;以下是一些有效的策略&#xff1a; 新闻策略…

学点儿Java_Day7_继承、重载、重写、多态、抽象类

1 继承 1.1 概念与理解 继承&#xff1a; 你继承谁你就是谁&#xff0c;继承是一种严格的父子关系&#xff08;抽取到父类里面的属性和方法一定是所有子类所共有&#xff09;      &#xff08;Student继承Person&#xff0c;那么Student就是人&#xff09; 面向对象特征…

数据结构:初识树和二叉树

目前主流的方式是左孩子右兄弟表示法 我们的文件系统就是一个树 以上就是树的概念&#xff0c;我们今天还要来学习一种从树演变的重要的结构&#xff1a;二叉树 顾名思义二叉树就是一个结点最多有两个子树。 其中我们还要了解满二叉树和完全二叉树的概念 注意我们的完全二叉…

四、C语言中的数组:如何输入与输出二维数组(数组,完)

本章的学习内容如下 四、C语言中的数组&#xff1a;数组的创建与初始化四、C语言中的数组&#xff1a;数组的输入与元素个数C语言—第6次作业—十道代码题掌握一维数组四、C语言中的数组&#xff1a;二维数组 1.二维数组的输入与输出 当我们输入一维数组时需要一个循环来遍历…

10.注册页面

注册页面 在pages中新建页面register 复制粘贴之前的登录页面 设置上传头像图片 微信官方文档 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/userProfile.html <button class"avatar-wrapper" open-type"chooseAvatar&quo…

微调alpaca-lora遇到的一些问题

1、环境简介 环境&#xff1a; 系统&#xff1a;Ubuntu torch&#xff1a;2.2.1 python&#xff1a;3.10 gpu&#xff1a;V100 16g peft&#xff1a;0.9.0 使用PEFT中的lora方式微调llama-2-7b-hf&#xff0c;项目地址&#xff1a;alpaca-lora 2、混合精度训练Tensor相互计算会…

海外重要行业媒体:知名服务商IntoTheBlock现已集成波场TRON网络分析数据

近日,领先链上分析服务提供商 IntoTheBlock 宣布已将波场 TRON 网络集成至其市场情报套件。该合作引发多家海外加密媒体关注,Crypto Slate、Crypto Briefing等均对此进行了报道,称此次合作意义深远,能帮助数百万用户更深入地了解波场TRON生态系统。 报道表示,波场TRON网络规模大…

中文编程入门(Lua5.4.6中文版)第十一章 Lua 模块与包 参考星争际霸游戏

在遥远的星争际霸世界中&#xff0c;代码模块就如同星际基地中的高科技仓库&#xff0c;储存着各类经过封装优化的战术指令和战略资源。自Lua 5.1版本起&#xff0c;星际编程者们引入了标准化的模块管理系统&#xff0c;使得不同战舰之间能够共享和调用核心战斗算法&#xff0c…

Linux-网络层IP协议、链路层以太网协议解析

目录 网络层&#xff1a;IP协议地址管理路由选择 链路层 网络层&#xff1a; 网络层&#xff1a;负责地址管理与路由选择 — IP协议&#xff0c;地址管理&#xff0c;路由选择 IP协议 数据格式&#xff1a; 4位协议版本&#xff1a;4-ipv4协议版本 4位首部长度&#xff1a;以…

Redis实战:缓存穿透及其解决思路 实战演示

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏Redis实战与进阶 本专栏讲解Redis从原理到实践 …

Python爬虫框架大比拼!从小爬虫到大数据采集|电商大数据采集API接口

在互联网时代&#xff0c;数据是无处不在的黄金。无论你是寻找小规模的数据采集任务还是大规模的网络爬虫项目&#xff0c;Python提供了丰富的爬虫框架供你选择。对于小型爬虫需求&#xff0c;你可能会喜欢使用requests库和Beautiful Soup(bs4库)这样的基本工具&#xff0c;它们…

想当初级爬虫工程师,需要把爬虫学到什么程度?

这篇文章会说说我自己的心得体验&#xff0c;关于爬虫、关于工作&#xff0c;仅供参考。 学到哪种程度 暂且把目标定位初级爬虫工程师&#xff0c;简单列一下吧&#xff1a; &#xff08;必要部分&#xff09; 语言选择&#xff1a;一般是了解Python、Java、Golang之一 熟悉…

C#操作像素替换图片中的指定颜色

待处理的图片&#xff0c;其特征是包含有限数量颜色&#xff0c;不同的颜色相互交叉使用&#xff0c;相同颜色并未完全连贯&#xff0c;需要将图片中的指定颜色替换为另一颜色。虽然很多图片处理工具都支持类似操作&#xff0c;最后还是自己动手编写简单的处理程序。   程序的…

尚硅谷SQL|数据库的创建,修改与删除

DDL&#xff1a;创建和管理表 DDL所有的操作都要慎重&#xff0c;尤其是删除&#xff0c;清空等。 创建数据库--->确认字段--->创建数据表---->插入数据 创建数据库 1.创建数据库&#xff1a;推荐使用方式3 #创建数据库 #方式1,使用的是默认字符集 create databa…