精读《React 高阶组件》

本期精读文章是:React Higher Order Components in depth

1 引言

高阶组件( higher-order component ,HOC )是 React 中复用组件逻辑的一种进阶技巧。它本身并不是 React 的 API,而是一种 React 组件的设计理念,众多的 React 库已经证明了它的价值,例如耳熟能详的 react-redux。

高阶组件的概念其实并不难,我们能通过类比高阶函数迅速掌握。高阶函数是把函数作为参数传入到函数中并返回一个新的函数。这里我们把函数替换为组件,就是高阶组件了。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

当然了解高阶组件的概念只是万里长征第一步,精读文章在阐述其概念与实现外,也强调了其重要性与局限性,以及与其他方案的比较,让我们一起来领略吧。

2 内容概要

高阶组件常见有两种实现方式,一种是 Props Proxy,它能够对 WrappedComponent 的 props 进行操作,提取 WrappedComponent state 以及使用其他元素来包裹 WrappedComponent。Props Proxy 作为一层代理,具有隔离的作用,因此传入 WrappedComponent 的 ref 将无法访问到其本身,需要在 Props Proxy 内完成中转,具体可参考以下代码,react-redux 也是这样实现的。

此外各个 Props Proxy 的默认名称是相同的,需要根据 WrappedComponent 来进行不同命名。

function ppHOC(WrappedComponent) {return class PP extends React.Component {// 实现 HOC 不同的命名static displayName = `HOC(${WrappedComponent.displayName})`;getWrappedInstance() {return this.wrappedInstance;}// 实现 ref 的访问setWrappedInstance(ref) {this.wrappedInstance = ref;}render() {return <WrappedComponent {...this.props,ref: this.setWrappedInstance.bind(this),} />}}
}@ppHOC
class Example extends React.Component {static displayName = 'Example';handleClick() { ... }...
}class App extends React.Component {handleClick() {this.refs.example.getWrappedInstance().handleClick();}render() {return (<div><button onClick={this.handleClick.bind(this)}>按钮</button><Example ref="example" /></div>  );}
}

另一种是 Inheritance Inversion,HOC 类继承了 WrappedComponent,意味着可以访问到 WrappedComponent 的 state、props、生命周期和 render 等方法。如果在 HOC 中定义了与 WrappedComponent 同名方法,将会发生覆盖,就必须手动通过 super 进行调用了。通过完全操作 WrappedComponent 的 render 方法返回的元素树,可以真正实现渲染劫持。这种方案依然是继承的思想,对于 WrappedComponent 也有较强的侵入性,因此并不常见。

function ppHOC(WrappedComponent) {return class ExampleEnhance extends WrappedComponent {...componentDidMount() {super.componentDidMount();}componentWillUnmount() {super.componentWillUnmount();}render() {...return super.render();}}
}

3 精读

本次提出独到观点的同学有:
@monkingxue @alcat2008 @淡苍 @camsong,精读由此归纳。

HOC 的适用范围

对比 HOC 范式 compose(render)(state) 与父组件(Parent Component)的范式 render(render(state)),如果完全利用 HOC 来实现 React 的 implement,将操作与 view 分离,也未尝不可,但却不优雅。HOC 本质上是统一功能抽象,强调逻辑与 UI 分离。但在实际开发中,前端无法逃离 DOM ,而逻辑与 DOM 的相关性主要呈现 3 种关联形式:

  • 与 DOM 相关,建议使用父组件,类似于原生 HTML 编写
  • 与 DOM 不相关,如校验、权限、请求发送、数据转换这类,通过数据变化间接控制 DOM,可以使用 HOC 抽象
  • 交叉的部分,DOM 相关,但可以做到完全内聚,即这些 DOM 不会和外部有关联,均可

DOM 的渲染适合使用父组件,这是 React JSX 原生支持的方式,清晰易懂。最好是能封装成木偶组件(Dumb Component)。HOC 适合做 DOM 不相关又是多个组件共性的操作。如 Form 中,validator 校验操作就是纯数据操作的,放到了 HOC 中。但 validator 信息没有放到 HOC 中。但如果能把 Error 信息展示这些逻辑能够完全隔离,也可以放到 HOC 中(可结合下一小节 Form 具体实践详细了解)。
数据请求是另一类 DOM 不相关的场景,react-refetch 的实现就是使用了 HOC,做到了高效和优雅:

connect(props => ({usersFetch: `/users?status=${props.status}&page=${props.page}`,userStatsFetch: { url: `/users/stats`, force: true }
}))(UsersList)

HOC 的具体实践

HOC 在真实场景下的运行非常多,之前笔者在 基于 Decorator 的组件扩展实践 一文中也提过使用高阶组件将更细粒度的组件组合成 Selector 与 Search。结合精读文章,这次让我们通过 Form 组件的抽象来表现 HOC 具有的良好扩展机制。

Form 中会包含各种不同的组件,常见的有 Input、Selector、Checkbox 等等,也会有根据业务需求加入的自定义组件。Form 灵活多变,从功能上看,表单校验可能为单组件值校验,也可能为全表单值校验,可能为常规检验,比如:非空、输入限制,也可能需要与服务端配合,甚至需要根据业务特点进行定制。从 UI 上看,检验结果显示的位置,可能在组件下方,也可能是在组件右侧。

直接裸写 Form,无疑是机械而又重复的。将 Form 中组件的 value 经过 validator,把 value,validator 产生的 error 信息储存到 state 或 redux store 中,然后在 view 层完成显示。这条路大家都是相同的,可以进行复用,只是我们面对的是不同的组件,不同的 validator,不同的 view 而已。对于 Form 而言,既要满足通用,又要满足部分个性化的需求,以往单纯的配置化只会让使用愈加繁琐,我们所需要抽象的是 Form 功能而非 UI,因此通过 HOC 针对 Form 的功能进行提取就成为了必然。

image

至于 HOC 在 Form 上的具体实现,首先将表单中的组件(Input、Selector…)与相应 validator 与组件值回调函数名(trigger)传入 Decorator,将 validator 与 trigger 相绑定。Decorator 完成了各种不同组件与 Form 内置 Store 间 value 的传递、校验功能的抽象,即精读文章中提到 Props Proxy 方式的其中两种作用:提取 state操作 props

function formFactoryFactory({validator,trigger = 'onChange',...
}) {return FormFactory(WrappedComponent) {return class Decorator extends React.Component {getBind(trigger, validator) {...}render() {const newProps = {...this.props,[trigger]: this.getBind(trigger, validator),...}return <WrappedComponent {...newProps} />}}}
}// 调用
formFactoryFactory({validator: (value) => {return value !== '';}
})(<Input placeholder="请输入..." />)

当然为了考虑个性化需求,Form Store 也向外暴露很多 API,可以直接获取和修改 value、error 的值。现在我们需要对一个表单的所有值提交到后端进行校验,根据后端返回,分别列出各项的校验错误信息,就需要借助相应项的 setError 去完成了。

这里主要参考了 rc-form 的实现方式,有兴趣的读者可以阅读其源码。

import { createForm } from 'rc-form';class Form extends React.Component {submit = () => {this.props.form.validateFields((error, value) => {console.log(error, value);});}render() {const { getFieldError, getFieldDecorator } = this.props.form;const errors = getFieldError('required');return (<div>{getFieldDecorator('required', {rules: [{ required: true }],})(<Input />)}{errors ? errors.join(',') : null}<button onClick={this.submit}>submit</button></div>);}
}export createForm()(Form);

4 总结

React 始终强调组合优于继承的理念,期望通过复用小组件来构建大组件使得开发变得简单而又高效,与传统面向对象思想是截然不同的。高阶函数(HOC)的出现替代了原有 Mixin 侵入式的方案,对比隐式的 Mixin 或是继承,HOC 能够在 Devtools 中显示出来,满足抽象之余,也方便了开发与测试。当然,不可过度抽象是我们始终要秉持的原则。希望读者通过本次阅读与讨论,能结合自己具体的业务开发场景,获得一些启发。

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

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

相关文章

【QT+QGIS跨平台编译】之五十三:【QGIS_CORE跨平台编译】—【qgssqlstatementparser.cpp生成】

文章目录 一、Bison二、生成来源三、构建过程一、Bison GNU Bison 是一个通用的解析器生成器,它可以将注释的无上下文语法转换为使用 LALR (1) 解析表的确定性 LR 或广义 LR (GLR) 解析器。Bison 还可以生成 IELR (1) 或规范 LR (1) 解析表。一旦您熟练使用 Bison,您可以使用…

2024年个人护理赛道选品风向在哪?这份赛盈分销选品攻略必看!

2024年还会卷下去吗&#xff1f;看到一位行业大佬分享的内容深有感触&#xff1a;坚定做好产品&#xff0c;不做大卖&#xff0c;就不存在卷不卷。 有人出局&#xff0c;也会有人入局&#xff0c;并且深耕领域做大做强。 专注口腔护理的Bitvae入行不到两年&#xff0c;凭借一款…

C#学习(十四)——垃圾回收、析构与IDisposable

一、何为GC 数据是存储在内存中的&#xff0c;而内存又分为Stack栈内存和Heap堆内存 Stack栈内存Heap堆内存速度快、效率高结构复杂类型、大小有限制对象只能保存简单的数据引用数据类型基础数据类型、值类型- 举个例子 var c new Customer{id: 123,name: "Jack"…

微信小程序手势冲突?不存在的!

原生的应用经常会有页面嵌套列表&#xff0c;滚动列表能够改变列表大小&#xff0c;然后还能支持列表内下拉刷新等功能。看了很多的小程序好像都没有这个功能&#xff0c;难道这个算是原生独享的吗&#xff0c;难道是由于手势冲突无法实现吗&#xff0c;冷静的思考了一下&#…

【InternLM 实战营笔记】XTuner 大模型单卡低成本微调实战

XTuner概述 一个大语言模型微调工具箱。由 MMRazor 和 MMDeploy 联合开发。 支持的开源LLM (2023.11.01) InternLM Llama&#xff0c;Llama2 ChatGLM2&#xff0c;ChatGLM3 Qwen Baichuan&#xff0c;Baichuan2 Zephyr 特色 傻瓜化&#xff1a; 以 配置文件 的形式封装了大…

看待事物的层与次 | DBA与架构的一次对话交流

前言 在计算机软件业生涯中,想必行内人或多或少都能感受到系统架构设计与数据库系统工程的重要性,也能够清晰地认识到在计算机软件行业中技术工程师这个职业所需要的专业素养和必备技能! 背景 通过自研的数据库监控管理工具,发现 SQL Server 数据库连接数在1-2K之间,想…

Yii2中如何使用scenario场景,使rules按不同运用进行字段验证

Yii2中如何使用scenario场景&#xff0c;使rules按不同运用进行字段验证 当创建news新闻form表单时&#xff1a; 添加新闻的时候执行create动作。 必填字段&#xff1a;title-标题&#xff0c;picture-图片&#xff0c;description-描述。 这时候在model里News.php下rules规则…

利用coze 搭建“全功能“微信客服(2)

紧跟上篇 利用coze 搭建"全功能"微信客服&#xff08;1&#xff09;&#xff0c;不知道来龙去脉自行查阅 先表扬下coze: coze 是国内少数开放平台之一&#xff0c;里面提供各种插件还可以开发工作流&#xff0c;让你可以实现多模态全功能大模型 吐槽 没有API开放接口…

2024亚马逊全球开店注册前需要准备什么?

在2023年出海四小龙SHEIN、Temu、速卖通AliExpress、TikTok Shop快速增长扩张&#xff0c;成为了中国跨境卖家“逃离亚马逊”的新选择。但是&#xff0c;跨境电商看亚马逊。当前&#xff0c;亚马逊仍然是跨境电商行业的绝对老大&#xff0c;占有将近70%成以上的业务份额。 作为…

分享fastapi低级错误

我是创建表的时候把__tablename__ 写成__table__然后一直报这个错误

端游如何防破解

在2023年这个游戏大年中&#xff0c;诸多热门大作涌现&#xff0c;作为世界级IP哈利哈利波特的衍生游戏——《霍格沃茨之遗》毫无悬念地成为2023年游戏圈的首款爆款作品&#xff0c;斩获了一众玩家的青睐。 在众多光环的加持下&#xff0c;《霍格沃茨之遗》很快被著名游戏破解…

深入 Starknet 去中心化世界,探秘实用开发利器

Starknet 近期开放空投&#xff0c;面向 130 万地址总量发放超 7 亿枚 Token&#xff0c;让 ECMP 早期贡献者、GitHub 开源开发者、Starknet 用户等各个层面的生态参与者都得以深度参与。 盛宴的背后&#xff0c;是 Starknet 正迎来发展的关键机遇。在今年以太坊坎昆升级的背景…

【漏洞复现】某厂商上网行为管理系统static_convert命令执行漏洞

Nx01 产品简介 天融信上网行为管理系统是天融信公司凭借多年来的安全产品研发经验&#xff0c;为满足各行各业进行网络行为管理和内容审计的专业产品。 Nx02 漏洞描述 天融信上网行为管理系统老版本static_convert.php接口存在RCE漏洞&#xff0c;攻击者利用此漏洞可以获取服务…

超强预测算法:XGBoost预测模型

目录 往期精彩内容&#xff1a; 多变量特征序列、单序列数据预测实战 前言 1 风速数据预处理与数据集制作 1.1 导入数据 1.2 多变量数据预处理与数据集制作 1.3 单序列数据预处理与数据集制作 2超强模型XGBoost——原理介绍 3 模型评估和对比 3.1 随机森林预测模型 3…

基于NeRF/Gaussian的全新SLAM算法

什么是SLAM&#xff1f; SLAM&#xff0c;即同时定位与地图构建技术&#xff0c;SLAM可以让机器人、无人机和其他自动化系统能够在未知环境中同时进行自我定位和环境映射。 为什么是NeRF-Based SLAM&#xff1f; 传统CG将输入图像重新投影再融合到新的视图摄像机中&#xff0c…

InfiniBand 200Gbps QSFP56 高速线缆/光缆和光模块解决方案

随着数据中心和人工智能迅速发展&#xff0c;对高速、低延迟和低功耗的数据传输需求变得至关重要。飞速&#xff08;FS&#xff09;提供针对各种高性能计算场景量身定制的各种InfiniBand线缆和光模块产品。本文旨在概述飞速&#xff08;FS&#xff09;200G InfiniBand HDR 光缆…

MSCKF2讲:JPL四元数与Hamilton四元数

MSCKF2讲&#xff1a;JPL四元数与Hamilton四元数 文章目录 MSCKF2讲&#xff1a;JPL四元数与Hamilton四元数2 JPL四元数2.1 定义与区别2.2 JPL四元数的乘法2.3 反对称矩阵2.4 Ω ( ω ) \Omega(\omega) Ω(ω)矩阵2.5 JPL四元数与旋转矩阵的转换2.6 JPL四元数导数2.7 JPL四元数…

SpringCloud搭建微服务之Consul服务注册与发现

1. Consul介绍 Consul是由HashiCorp公司使用Go语言开发的一款开源工具&#xff0c;主要用于实现分布式系统的服务发现和服务配置&#xff0c;其内置了服务注册与发现框架、分布式一致性协议实现、健康检查、Key-Value存储、多数据中心方案。Consul具有高可移植性&#xff0c;支…

【数仓】Hadoop软件安装及使用(集群配置)

一、环境准备 1、准备3台虚拟机 Hadoop131&#xff1a;192.168.56.131Hadoop132&#xff1a;192.168.56.132Hadoop133&#xff1a;192.168.56.133 本例系统版本 CentOS-7.8&#xff0c;已安装jdk1.8 2、hosts配置&#xff0c;关闭防火墙 vi /etc/hosts添加如下内容&#x…

生成式AI设计模式:综合指南

原文地址&#xff1a;Generative AI Design Patterns: A Comprehensive Guide 使用大型语言模型 (LLM) 的参考架构模式和心理模型 2024 年 2 月 14 日 对人工智能模式的需求 我们在构建新事物时&#xff0c;都会依赖一些经过验证的方法、途径和模式。对于软件工程师来说&am…