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

借鉴自极客时间《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的sizegetSize() {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);};}, []);  // 空的依赖项数组意味着此效果只会运行一次,类似于类组件中的 componentDidMountreturn 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.mzph.cn/news/761817.shtml

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

相关文章

基于python+vue的幼儿园管理系统flask-django-php-nodejs

随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种活动信息、课程信息、菜谱信息、通知公告、家…

网站安全:抵御网络攻击的几个安全措施

随着互联网的发展&#xff0c;网站安全成为每个企业和个人都必须重视的问题。黑客攻击不断增多&#xff0c;威胁着网站的稳定性和数据的安全。为了应对这一挑战&#xff0c;我们必须采取一系列的安全措施&#xff0c;确保网站的安全与稳定。 不过&#xff0c;关于在做好网络安全…

Docker基本操作

我们可以通过 docker --help 查看所有命令 我们可以通过docker hub 拉取 Docker hub官网链接 接着 输入 docker images 查看刚才拉取的nginx镜像 镜像常见操作 docker imagesdocker rmidocker pulldocker pushdocker savedocker load

Docker 搭建私人仓库

docker 搭建私人仓库有下面几种方式&#xff1a; 1、docker hub 官方私人镜像仓库2、本地私有仓库 官方私人镜像仓库搭建很简单(就是需要有魔法&#xff0c;否则就异步到第二种方法吧)&#xff0c;只需要 login、pull、tag、push 几种命令就完事了。而本地私人镜像仓库则比较麻…

sentinel整合gateway实现服务限流

导入依赖: <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><…

【Python脚本随手笔记】 ---基于鸿蒙系统LiteOS实现差分编译脚本(下篇)

💌 所属专栏:【Python脚本随手笔记】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘 文章目录 前言一、&…

mac下Appuim环境安装-持续更新中

参考资料 Mac安装Appium_mac电脑安装appium-CSDN博客 安卓测试工具&#xff1a;Appium 环境安装&#xff08;mac版本&#xff09;_安卓自动化测试mac环境搭建-CSDN博客 1. 基本环境依赖 1 node.js 2 JDK&#xff08;Java JDK&#xff09; 3 Android SDK 4 Appium&#x…

如何优化前端项目的 SEO

在当今数字化时代&#xff0c;网站对于企业的重要性不言而喻。然而&#xff0c;一个优秀的网站如果在搜索引擎中排名靠后&#xff0c;将无法吸引到足够的流量和用户。因此&#xff0c;优化前端项目的SEO已经成为了网站拓展业务、提升品牌知名度的必经之路。 响应式设计与移动优…

ByteMD - 掘金社区 MarkDown 编辑器的免费开源的版本,可以在 Vue / React / Svelte 中使用

各位元宵节快乐&#xff0c;今天推荐一款字节跳动旗下掘金社区官方出品的 Markdown 编辑器 JS 开发库。 ByteMD 是一个用于 web 开发的 Markdown 编辑器 JavaScript 库&#xff0c;是字节跳动&#xff08;也就是掘金社区&#xff09;出品的 Markdown 格式的富文本编辑器&#…

利用Python进行数据清洗与预处理:Pandas的高级用法【第147篇—Pandas的高级用法】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行数据清洗与预处理&#xff1a;Pandas的高级用法 在数据科学和机器学习领域&…

AI绘画自动生成器:让艺术创作触手可及

随着人工智能技术的飞速发展&#xff0c;越来越多的应用领域逐渐与AI技术融合。在艺术领域&#xff0c;AI绘画自动生成器成为了一款备受关注的产品。它利用深度学习算法&#xff0c;让用户通过输入关键词或描述性文本&#xff0c;就能在几秒钟内生成一幅独特的艺术作品。在这篇…

选择器加练习

一、常用的选择器 1.元素选择器 语法 : 标签名{} 作用 : 选中对应标签中的内容 例:p{} , div{} , span{} , ol{} , ul{} ...... 2.类选择器(class选择器) 语法 : .class属性值{} 作用 : 选中对应class属性值的元素 注意:class里面的属性值不能以数字开头,如果以符号开头,…

Java中最简单的添加日志链路的方式之一

Java项目中添加日志链路功能的设计与实现 文章目录 Java项目中添加日志链路功能的设计与实现前言一、日志链路的概念与作用二、添加日志链路的设计思路三、如何支持多线程下的日志打印也附加上日志链路id1. 示例1&#xff1a;实现Runnable接口&#xff0c;无返回值2. 示例2&…

适配器模式与桥接模式-灵活应对变化的两种设计策略大比拼

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;设计模式深度解析&#xff1a;适配器模式与桥接模式-灵活应对变…

Spark Rebalance hint的倾斜的处理(OptimizeSkewInRebalancePartitions)

背景 本文基于Spark 3.5.0 目前公司在做小文件合并的时候用到了 Spark Rebalance 这个算子&#xff0c;这个算子的主要作用是在AQE阶段的最后写文件的阶段进行小文件的合并&#xff0c;使得最后落盘的文件不会太大也不会太小&#xff0c;从而达到小文件合并的作用&#xff0c;…

sentinel熔断规则详解

1、慢调用降级熔断 1.1、参数详解 最大RT&#xff1a;调用接口的最大时间。 比例阈值&#xff1a;超过了最大RT调用时间的请求的比例。 熔断时长&#xff1a;触发熔断后&#xff0c;熔断的时间 最小请求数据&#xff1a;每秒最少的请求数量&#xff0c;只有大于等于这个数…

SQLiteC/C++接口详细介绍sqlite3_stmt类(九)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;六&#xff09; 下一篇&#xff1a; 无 33、sqlite3_column_table_name 函数 sqlite3_column_table_name 用于返回结果集中指定列所属的表的名称。如果查询中列使…

前端案例:产品模块

文章目录 产品模块效果结构布局分析父级盒子布局图片和段落评价和详情 产品模块效果 结构布局分析 1、大的父级盒子包含全部的内容 2、内容装入 图片&#xff08;img标签&#xff09;&#xff1b;分别三个子盒子装入两段评价以及商品信息。 父级盒子布局 div {width: 300px…

网络通信——IP地址、端口号、协议(TCP、UDP)

通信架构 网络通信三要素 IP地址 IPv4地址 IPv6地址 IP域名 IP常识 端口号 概念 协议 开放式网络互联标准&#xff1a;OSI、TCP/IP 传输层的2个通信协议——UDP、TCP TCP协议&#xff1a;三次握手建立建立可靠连接 进行三次握手的原因&#xff1a;为了确保客户端和服务端…

实时数仓之实时数仓架构(Doris)

目前比较流行的实时数仓架构有两类,其中一类是以Flink+Doris为核心的实时数仓架构方案;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对Flink+Doris架构进行介绍,这套架构的特点是组件涉及相对较少,架构简单,实时性更高,且易于Lambda架构实现,Doris本身可以支…