【React系列】高阶组件
本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)
一. 高阶组件
1.1. 认识高阶组件
什么是高阶组件呢?相信很多同学都听说过,也用过 高阶函数,它们非常相似,所以我们可以先来回顾一下什么是 高阶函数。
高阶函数的维基百科定义:至少满足以下条件之一:
- 接受一个或多个函数作为输入;
- 输出一个函数;
JavaScript中比较常见的filter、map、reduce
都是高阶函数。
那么什么是高阶组件呢?
- 高阶组件的英文是 Higher-Order Components,简称为 HOC;
- 官方的定义:高阶组件是参数为组件,返回值为新组件的函数;
我们可以进行如下的解析:
- 首先, 高阶组件 本身不是一个组件,而是一个函数;
- 其次,这个函数的参数是一个组件,返回值也是一个组件;
高阶组件的调用过程类似于这样:
const EnhancedComponent = higherOrderComponent(WrappedComponent);
高阶函数的编写过程类似于这样:
function higherOrderComponent(WrapperComponent) {return class NewComponent extends PureComponent {render() {return <WrapperComponent/>}}
}
组件的名称问题:
- 在ES6中,类表达式中类名是可以省略的,所以可以可以写成下面的写法:
function higherOrderComponent(WrapperComponent) {return class extends PureComponent {render() {return <WrapperComponent/>}}
}
- 另外,组件的名称都可以通过
displayName
来修改:
完整的代码,我们可以这样来编写:
import React, { PureComponent } from 'react';function higherOrderComponent(WrapperComponent) {return class NewComponent extends PureComponent {render() {return <WrapperComponent/>}}
}class App extends PureComponent {render() {return (<div>App</div>)}
}export default higherOrderComponent(App);
高阶组件并不是React API的一部分,它是基于React的组合特性而形成的设计模式;
高阶组件在一些React第三方库中非常常见:
- 比如redux中的connect;
- 比如
react-router
中的withRouter
;
在我们的开发中,高阶组件可以帮助我们做哪些事情呢?
1.2. 高阶组件的使用
1.2.1. props的增强
不修改原有代码的情况下,添加新的props
假如我们有如下案例:
class Header extends PureComponent {render() {const { name, age } = this.props;return <h2>Header {name + age}</h2>}
}export default class App extends PureComponent {render() {return (<div><Header name="aaa" age={18} /></div>)}
}
我们可以通过一个高阶组件,让使用者在不破坏原有结构的情况下对某个组件增强props
:
function enhanceProps(WrapperCpn, otherProps) {return props => <WrapperCpn {...props} {...otherProps} />
}const EnhanceHeader = enhanceProps(Header, {height: 1.88})
- 有点拦截器和java动态代理的意思
利用高阶组件来共享 Context 属性
import React, { PureComponent, createContext } from 'react';const UserContext = createContext({nickname: "默认",level: -1
})function Header(props) {return (<UserContext.Consumer>{value => {const { nickname, level } = value;return <h2>Header {"昵称:" + nickname + "等级" + level}</h2>}}</UserContext.Consumer>)
}function Footer(props) {return (<UserContext.Consumer>{value => {const { nickname, level } = value;return <h2>Footer {"昵称:" + nickname + "等级" + level}</h2>}}</UserContext.Consumer>)
}const EnhanceHeader = enhanceProps(Header, { height: 1.88 })export default class App extends PureComponent {render() {return (<div><UserContext.Provider value={{ nickname: "why", level: 90 }}><Header /><Footer /></UserContext.Provider></div>)}
}
利用高阶组件withUser
:
import React, { PureComponent, createContext } from 'react';const UserContext = createContext({nickname: "默认",level: -1
})function withUser(WrapperCpn) {return props => {return (<UserContext.Consumer>{value => {return <WrapperCpn {...props} {...value}/>}}</UserContext.Consumer>)}
}function Header(props) {const { nickname, level } = props;return <h2>Header {"昵称:" + nickname + "等级:" + level}</h2>
}function Footer(props) {const { nickname, level } = props;return <h2>Footer {"昵称:" + nickname + "等级:" + level}</h2>
}const UserHeader = withUser(Header);
const UserFooter = withUser(Footer);export default class App extends PureComponent {render() {return (<div><UserContext.Provider value={{ nickname: "why", level: 90 }}><UserHeader /><UserFooter /></UserContext.Provider></div>)}
}
1.2.2. 利用高阶组件进行鉴权判断
在开发中,我们可能遇到这样的场景:
- 某些页面是必须用户登录成功才能进行进入;
- 如果用户没有登录成功,那么直接跳转到登录页面;
这个时候,我们就可以使用高阶组件来完成鉴权操作:
function LoginPage() {return <h2>LoginPage</h2>
}function CartPage() {return <h2>CartPage</h2>
}export default class App extends PureComponent {render() {return (<div><CartPage/></div>)}
}
编写鉴权的高阶组件:
function loginAuth(Page) {return props => {if (props.isLogin) {return <Page/>} else {return <LoginPage/>}}
}
完整的代码如下:
import React, { PureComponent } from 'react';function loginAuth(Page) {return props => {if (props.isLogin) {return <Page/>} else {return <LoginPage/>}}
}function LoginPage() {return <h2>LoginPage</h2>
}function CartPage() {return <h2>CartPage</h2>
}const AuthCartPage = loginAuth(CartPage);export default class App extends PureComponent {render() {return (<div><AuthCartPage isLogin={true}/></div>)}
}
1.2.3. 生命周期劫持
import React, { PureComponent } from 'react';class Home extends PureComponent {UNSAFE_componentWillMount() {this.begin = Date.now();}componentDidMount() {this.end = Date.now();const interval = this.end - this.begin;console.log(`Home渲染使用时间:${interval}`)}render() {return (<div><h2>Home</h2><p>我是home的元素,哈哈哈</p></div>)}
}class Detail extends PureComponent {UNSAFE_componentWillMount() {this.begin = Date.now();}componentDidMount() {this.end = Date.now();const interval = this.end - this.begin;console.log(`Detail渲染使用时间:${interval}`)}render() {return (<div><h2>Detail</h2><p>我是detail的元素,哈哈哈</p></div>)}
}export default class App extends PureComponent {render() {return (<div><Home/><Detail/></div>)}
}
我们可以定义如下高阶组件:
function logRenderTime(WrapperCpn) {return class extends PureComponent {UNSAFE_componentWillMount() {this.begin = Date.now();}componentDidMount() {this.end = Date.now();const interval = this.end - this.begin;console.log(`Home渲染使用时间:${interval}`)}render() {return <WrapperCpn {...this.props}/>}}
}const LogHome = logRenderTime(Home);
const LogDetail = logRenderTime(Detail);
完整代码如下:
import React, { PureComponent } from 'react';function logRenderTime(WrapperCpn) {return class extends PureComponent {UNSAFE_componentWillMount() {this.begin = Date.now();}componentDidMount() {this.end = Date.now();const interval = this.end - this.begin;console.log(`${WrapperCpn.name}渲染使用时间:${interval}`)}render() {return <WrapperCpn {...this.props}/>}}
}class Home extends PureComponent {render() {return (<div><h2>Home</h2><p>我是home的元素,哈哈哈</p></div>)}
}class Detail extends PureComponent {render() {return (<div><h2>Detail</h2><p>我是detail的元素,哈哈哈</p></div>)}
}const LogHome = logRenderTime(Home);
const LogDetail = logRenderTime(Detail);export default class App extends PureComponent {render() {return (<div><LogHome /><LogDetail /></div>)}
}
这个写法其实就是提取公共代码进行复用而已。
1.3. 高阶函数的意义
我们会发现利用高阶组件可以针对某些React代码进行更加优雅的处理。
其实早期的React有提供组件之间的一种复用方式是mixin
,目前已经不再建议使用:
Mixin
可能会相互依赖,相互耦合,不利于代码维护- 不同的
Mixin
中的方法可能会相互冲突 Mixin
非常多时,组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性
当然,HOC也有自己的一些缺陷:
- HOC需要在原组件上进行包裹或者嵌套,如果大量使用HOC,将会产生非常多的嵌套,这让调试变得非常困难;
- HOC可以劫持
props
,在不遵守约定的情况下也可能造成冲突;
Hooks的出现,是开创性的,它解决了很多React之前的存在的问题,比如this
指向问题、比如HOC的嵌套复杂度问题等等。
相关文章:

【React系列】高阶组件
本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 高阶组件 1.1. 认识高阶组件 什么是高阶组件呢?相信很多同学都听说过,也用过 高阶函数&…...

听GPT 讲Rust源代码--src/tools(38)
File: rust/src/tools/clippy/clippy_dev/src/lib.rs rust/src/tools/clippy/clippy_dev/src/lib.rs文件是Clippy开发工具的入口文件,其作用是提供Clippy开发过程中所需的功能和工具。Clippy是一个Rust代码的静态分析工具,用于提供各种有用的代码规范、编…...
.NET C# 如何获取object对象的数据
如何获取object对象的数据 在DAL层,一般会封装一些返回值,返回的类型就会为object ,但是需要其中的值进行判断 public static object SaveFileIns(string filepath){return new { path pathlist, file_name fileNamelist, Message "…...

使用IDEA创建使用 JDK8 的 2.x.x 版本的 Spring Boot 项目以及 Spring Boot 项目如何修改JDK版本
目录 一、在阿里云上官网上创建项目 二、将 IDEA 中创建项目的源地址修改为阿里云官网 三、创建 3.x.x 的项目之后修改配置降低至 2.7.x 版本和使用 JDK8(修改 Spring Boot 的 JDK 版本同理) 从上面的 Spring Boot 官网的截图中可以发现,自…...
游戏服务器整体架构思考
1.启动层 不管是单体架构还是微服务架构,其实服务器本身都是要启动的。 不管是用grpc实现远程调用,还是dubbo,还是说就一个简单的tcp监听,都是要启动的。 启动的时候,肯定要整合下controller接入层,不管是叫…...
labelme 标注的数据集转化为Mask-Rcnn适用的数据集
labelme 标注的数据集转化为Mask-Rcnn适用的数据集 食用步骤 1.labelme标注数据时,将生成的json文件和原图保存在一起 2.只需提供labelme生成的数据的文件夹,和maskrcnn的数据集文件夹,运行代码就会自动进行处理 3.代码会在提供的maskrcn…...

x-cmd pkg | tig - git 文本模式界面
目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 tig 由 Jonas Fonseca 于 2006 年使用 C 语言创建的 git 交互式文本命令行工具。旨在开启交互模式快速浏览 git 存储库的信息以及 git 命令的运行。 首次用户 使用 x tig 即可自动下载并使用 在终端运行 eval "…...

信息论与编码期末复习——概念论述简答题(一)
个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:V…...

[Kubernetes]4. 借助腾讯云TKE快速创建Pod、Deployment、Service部署k8s项目
前面讲解了通过命令行方式来部署k8s项目,下面来讲讲通过腾讯云TKE来快速创建Pod、Deployment、Service部署k8s项目,云平台搭建Kubernetes可参考[Kubernetes]1.Kubernetes(K8S)介绍,基于腾讯云的K8S环境搭建集群以及裸机搭建K8S集群 一.通过腾讯云TKE创建集群 1.创建集群 参考上…...
二叉排序树的创建、插入、查找和删除【数据结构】
二叉排序树 若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。它的左、右树又分为⼆叉排序树 二叉排序树也叫二叉查找树、二叉搜索树 二叉排序树的创建、插入、查找和删除 …...
【管理篇 / 恢复】❀ 07. macOS下用命令刷新固件 ❀ FortiGate 防火墙
【简介】随着苹果电脑的普及,很多管理员都会通过苹果电脑对飞塔防火墙进行管理。当防火墙需要命令状态下刷新固件时,在macOS下用命令刷新固件,将会是一个小小的挑战。 首先是硬件的连接,USB配置线的USB一头,接入MAC的U…...
工作纪实40-使用redis的几种姿势
线上查问题看某个redis的key值,记录一下 1.直接使用telnet进行连接(贼拉方便) telnet ip port > auth pwd1.模糊查询 scan 0 MATCH abc* 2.查看所有key keys * 3.ttl key 查看key的ttl2.使用redis-cli连接(费劲吧啦,还需要本地…...

修改 docker /dev/shm 的大小
修改 docker /dev/shm 的大小 1,获取完整id: docker inspect 245| grep Id rootlynxi:~# docker inspect 245| grep Id"Id": "245ab167ed9a79873b31b3a38df2053870fe72f267c3c1a660df25c63e37e88b",2,修改 ShmSize&…...

【观察】Aginode安捷诺:坚守“长期主义”,服务中国数字经济
毫无疑问,随着整个社会加速数字化转型,尤其是5G、人工智能、大数据等技术兴起,以及智慧医疗、智慧金融、智能制造等应用加速落地,算力网络在经济社会发展中扮演了愈来愈重要的角色,成为支撑数字经济蓬勃发展的“新引擎…...

HttpClient库与代理IP在爬虫程序中的应用
目录 前言 一、HttpClient库的基本使用方法 二、代理IP的使用方法 三、代理IP池的使用方法 四、总结 前言 在编写爬虫程序时,我们经常会使用HttpClient库来发送HTTP请求,获取网页内容。然而,有些网站可能会对频繁的请求进行限制&#x…...

C#最佳工具集合:IDE、分析、自动化工具等
C#是企业中广泛使用的编程语言,特别是那些依赖微软的程序语言。如果您使用C#构建应用程序,则最有可能使用Visual Studio,并且已经寻找了一些扩展来对您的开发进行管理。但是,这个工具列表可能会改变您编写C#代码的方式。 C#编程的…...

promethues grafana 安装和使用
文章目录 1、promethues安装2、node-exporter安装3、grafana安装4、配置promethues监控node节点5、grafana操作外传 Docker 镜像下载地址: https://hub.docker.com 比较好的hub.docker.com///-- https://hub.docker.com/u/bitnami grafana监控面板:https…...

华为DriveONE电机控制器拆解实拍
如果说之前的问界M5、M7,华为让我们看到其在智能化上确实拥有遥遥领先的能力,那么在智界S7上,则让我们看到华为在动力、底盘这些硬件执行层面,竟然也有不输给很多车企的实力。1、华为电驱,全球第一?在智界S…...
【git使用】历史commit的分割(git rebase和 git reset的联合使用)
参考 [译] 分割一个已存在的 git commit - 掘金Git - 重写历史idea git如何撤回提交 - PingCodegit 工作原理与撤销操作图解 | Shall We Code? 分割一个已存在的 git commit Git 与其他版本控制系统的主要区别之一,在于其允许用户重写历史。实现这一目的的主要途…...

栈和队列oj题——225. 用队列实现栈
** 个人主页:晓风飞 专栏: 数据结构| Linux|| C语言 路漫漫其修远兮,吾将上下而求索 文章目录 题目要求:实现 MyStack 类:注意:示例:解释:提示: 解题核心数据结构的定义初…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...