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 高阶组件相关推荐

  1. react实现汉堡_利用 React 高阶组件实现一个面包屑导航

    什么是 React 高阶组件 React 高阶组件就是以高阶函数的方式包裹需要修饰的 React 组件,并返回处理完成后的 React 组件.React 高阶组件在 React 生态中使用的非常频繁, ...

  2. [react] 高阶组件(HOC)有哪些优点和缺点?

    [react] 高阶组件(HOC)有哪些优点和缺点? HOC 优点 通过传递props去影响内层组件的状态,不直接改变内层组件的状态,降低了耦合度 缺点 组件多层嵌套, 增加复杂度与理解成本 ref隔 ...

  3. 「react进阶」一文吃透React高阶组件(HOC)

    一 前言 React高阶组件(HOC),对于很多react开发者来说并不陌生,它是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数.高阶作用用于强化 ...

  4. React高阶组件探究

    React高阶组件探究 在使用React构建项目的过程中,经常会碰到在不同的组件中需要用到相同功能的情况.不过我们知道,去这些组件中到处编写同样的代码并不优雅. 在以往,为了解决以上的情况,我们会用到 ...

  5. react组件类型及深入理解react高阶组件

    React中常见的组件类型及分类: 1.展示组件(Presentational component) 与 容器组件(Container component) 2.类组件(Class component ...

  6. React高阶组件_阶段1

    react高阶组件_阶段1 作用: 个人总结的高阶组件设计的作用主要有两点, 这里直接使用装饰器方式 非装饰器使用请结合我的博文"react基础梳理_阶段1"中的"自定义 ...

  7. react 高阶组件hoc使用

    react 高阶组件hoc使用 1. 为什么使用高阶组件 2. 具体使用 2.1原代码: 2.2 使用hoc封装后 1. 为什么使用高阶组件 高阶组件(HOC)是 React 中用于复用组件逻辑的一种 ...

  8. 使用react 高阶组件withRouter

    使用react 高阶组件withRouter withRouter 是一个高阶组件,把 match,location,history 三个对象注入到组件的 props 中.这是一个非常实用的函数 re ...

  9. react 高阶组件

    react 高阶组件 概述 目的:实现状态逻辑复用 采用 包装(装饰)模式,比如说:手机壳 手机:获取保护功能 手机壳:提供保护功能 高阶组件就相当于手机壳,通过包装组件,增强组件功能 思路分析 高阶 ...

最新文章

  1. CEikEdWin 类的使用
  2. python中的print()、str()和repr()的区别
  3. 在虚拟机装一个linux系统
  4. 32 岁大佬阿里二面,他答 JVM 题的姿势,令面试官很想不通。。。
  5. android 玩pc游戏,Shield掌机试玩: Android系统 可玩PC单机游戏
  6. 程序员必备的技能矩阵图
  7. Java核心技术卷1:基础知识(原书第10版)
  8. 多国电子书盈利模式分析
  9. JDK官网下载历史版本
  10. 交警计算机系统审计,公安移动警务审计及考核系统
  11. 45个经典蓝屏案例一一破解
  12. Android按键事件处理流程 -- KeyEvent
  13. 李嘉诚:孤独是他的能量
  14. 2020-04-08
  15. 游戏策划入门(1)——什么样的创意是能够实现的?
  16. 联想小新Air13高定黑使用初体验
  17. Python的wheel文件安装
  18. CentOS 7 安装 OTRS工单提交跟踪系统
  19. 计算机课堂教学的评价,课堂教学评价表
  20. python 语音识别 离线_语音识别离线语音识别,SpeechRecognition

热门文章

  1. 我,自学5个月拿到字节offer,双非,非科班,分享一下我是怎么过来的
  2. 五一出行欢乐多。。。。
  3. 搭建ss5+proxychains
  4. Golang状态设计模式(二十)
  5. java 直接从数组中获取最大值和最小值的方法
  6. 虚拟机的三种网络模式(Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式))
  7. Firefox与Chrome浏览器导入burpsuite证书
  8. PYthon 元组和列表的区别
  9. `MONGDB` 安装与使用
  10. nas存储如何做远程服务器数据备份_服务器数据远程备份