【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 类:注意:示例:解释:提示: 解题核心数据结构的定义初…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...