【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 类:注意:示例:解释:提示: 解题核心数据结构的定义初…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
