精读 React 高阶组件
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), } /> } }} @ppHOCclass 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 的功能进行提取就成为了必然。至于 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 中显示出来,满足抽象之余,也方便了开发与测试。当然,不可过度抽象是我们始终要秉持的原则。希望读者通过本次阅读与讨论,能结合自己具体的业务开发场景,获得一些启发。
精读 React 高阶组件相关推荐
- react实现汉堡_利用 React 高阶组件实现一个面包屑导航
什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁, ...
- [react] 高阶组件(HOC)有哪些优点和缺点?
[react] 高阶组件(HOC)有哪些优点和缺点? HOC 优点 通过传递props去影响内层组件的状态,不直接改变内层组件的状态,降低了耦合度 缺点 组件多层嵌套, 增加复杂度与理解成本 ref隔 ...
- 「react进阶」一文吃透React高阶组件(HOC)
一 前言 React高阶组件(HOC),对于很多react开发者来说并不陌生,它是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数.高阶作用用于强化 ...
- React高阶组件探究
React高阶组件探究 在使用React构建项目的过程中,经常会碰到在不同的组件中需要用到相同功能的情况.不过我们知道,去这些组件中到处编写同样的代码并不优雅. 在以往,为了解决以上的情况,我们会用到 ...
- react组件类型及深入理解react高阶组件
React中常见的组件类型及分类: 1.展示组件(Presentational component) 与 容器组件(Container component) 2.类组件(Class component ...
- React高阶组件_阶段1
react高阶组件_阶段1 作用: 个人总结的高阶组件设计的作用主要有两点, 这里直接使用装饰器方式 非装饰器使用请结合我的博文"react基础梳理_阶段1"中的"自定义 ...
- react 高阶组件hoc使用
react 高阶组件hoc使用 1. 为什么使用高阶组件 2. 具体使用 2.1原代码: 2.2 使用hoc封装后 1. 为什么使用高阶组件 高阶组件(HOC)是 React 中用于复用组件逻辑的一种 ...
- 使用react 高阶组件withRouter
使用react 高阶组件withRouter withRouter 是一个高阶组件,把 match,location,history 三个对象注入到组件的 props 中.这是一个非常实用的函数 re ...
- react 高阶组件
react 高阶组件 概述 目的:实现状态逻辑复用 采用 包装(装饰)模式,比如说:手机壳 手机:获取保护功能 手机壳:提供保护功能 高阶组件就相当于手机壳,通过包装组件,增强组件功能 思路分析 高阶 ...
最新文章
- CEikEdWin 类的使用
- python中的print()、str()和repr()的区别
- 在虚拟机装一个linux系统
- 32 岁大佬阿里二面,他答 JVM 题的姿势,令面试官很想不通。。。
- android 玩pc游戏,Shield掌机试玩: Android系统 可玩PC单机游戏
- 程序员必备的技能矩阵图
- Java核心技术卷1:基础知识(原书第10版)
- 多国电子书盈利模式分析
- JDK官网下载历史版本
- 交警计算机系统审计,公安移动警务审计及考核系统
- 45个经典蓝屏案例一一破解
- Android按键事件处理流程 -- KeyEvent
- 李嘉诚:孤独是他的能量
- 2020-04-08
- 游戏策划入门(1)——什么样的创意是能够实现的?
- 联想小新Air13高定黑使用初体验
- Python的wheel文件安装
- CentOS 7 安装 OTRS工单提交跟踪系统
- 计算机课堂教学的评价,课堂教学评价表
- python 语音识别 离线_语音识别离线语音识别,SpeechRecognition