react 高阶组件

概述

高级组件到底能够解决什么问题?举一个特别简单的例子,话说小明负责开发一个 web 应用,应用的结构如下所示,而且这个功能小明已经开发完了。

但是,有一天老板突然提出了一个权限隔离的需求,就是部分模块组件受到权限控制,后台的数据交互的结果权限控制着模块展示与否,而且没有权限会默认展示无权限提示页面。(如下图,黄色部分是受到权限控制的组件模块)

那么小明面临的问题是,如何给需要权限隔离的模块,绑定权限呢?那第一种思路是把所有的需要权限隔离的模块重新绑定权限,通过权限来判断组件是否展示。

这样无疑会给小明带来很多的工作量,而且后续项目可能还有受权限控制的页面或者组件,都需要手动绑定权限。那么如何解决这个问题呢,思考一下,既然是判断权限,那么可以把逻辑都写在一个容器里,然后将每个需要权限的组件通过容器包装一层,这样不就不需要逐一手动绑定权限了吗?所以 HOC 可以合理的解决这个问题,通过 HOC 模式结构如下图所示:

综上所述,HOC的产生根本作用就是解决大量的代码复用,逻辑复用问题。既然说到了逻辑复用,那么具体复用了哪些逻辑呢?

  • 首先第一种就是像上述的拦截问题,本质上是对渲染的控制,对渲染的控制可不仅仅指是否渲染组件,还可以像 dva 中 dynamic 那样懒加载/动态加载组件。
  • 还有一种场景,比如项目中想让一个非 Route 组件,也能通过 props 获取路由实现跳转,但是不想通过父级路由组件层层绑定 props ,这个时候就需要一个 HOC 把改变路由的 history 对象混入 props 中,于是 withRoute 诞生了。所以 HOC 还有一个重要的作用就是让 props 中混入一些你需要的东西。
  • 还有一种情况,如果不想改变组件,只是监控组件的内部状态,对组件做一些赋能,HOC 也是一个不错的选择,比如对组件内的点击事件做一些监控,或者加一次额外的生命周期

高阶函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

JavaScript中比较常见的filter、map、reduce都是高阶函数

高阶组件:

  • HOC (higher order component)
  • 高阶组件是参数为组件,返回值为新组件的函数
  • 在有了hook函数之后就很少用高阶组件了

我们可以进行如下的解析:

  • 首先, 高阶组件 本身不是一个组件,而是一个函数;
  • 其次,这个函数的参数是一个组件,返回值也是一个组件

 

使用高级组件的目的:

  • 目的:实现状态逻辑复用   增强一个组件的能力
  • 采用 包装(装饰)模式 ,比如说:手机壳
  • 手机:获取保护功能
  • 手机壳 :提供保护功能
  • 高阶组件就相当于手机壳,通过包装组件,增强组件功能

 

思路分析

  • 高阶组件(HOC,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件
  • 高阶组件的命名,通常: withMouse  withRouter withXXX
  • 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给
    被包装组件

使用步骤

  • 创建一个函数,名称约定以 with 开头
  • 指定函数参数(作为要增强的组件)  传入的组件只能渲染基本的UI
  • 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
  • 在内部创建的组件的render中,需要渲染传入的基本组件,增强功能,通过props的方式给基本组件传值
  • 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

两种不同的高阶组件

常用的高阶组件有属性代理和反向继承两种,两者之间有一些共性和区别。接下来分别介绍一下两种模式下的高阶组件。

属性代理

属性代理,就是用组件包裹一层代理组件,在代理组件上,可以做一些,对源组件的强化操作。这里注意属性代理返回的是一个新组件,被包裹的原始组件,将在新的组件里被挂载。

优点:

  • ① 属性代理可以和业务组件低耦合,零耦合,对于条件渲染和 props 属性增强,只负责控制子组件渲染和传递额外的 props 就可以了,所以无须知道,业务组件做了些什么。所以正向属性代理,更适合做一些开源项目的 HOC ,目前开源的 HOC 基本都是通过这个模式实现的。
  • ② 同样适用于类组件和函数组件。
  • ③ 可以完全隔离业务组件的渲染,因为属性代理说白了是一个新的组件,相比反向继承,可以完全控制业务组件是否渲染。
  • ④ 可以嵌套使用,多个 HOC 是可以嵌套使用的,而且一般不会限制包装 HOC 的先后顺序。

缺点:

  • ① 一般无法直接获取原始组件的状态,如果想要获取,需要 ref 获取组件实例。
  • ② 无法直接继承静态属性。如果需要继承需要手动处理,或者引入第三方库。
  • ③ 因为本质上是产生了一个新组件,所以需要配合 forwardRef 来转发 ref。

反向继承

反向继承和属性代理有一定的区别,在于包装后的组件继承了原始组件本身,所以此时无须再去挂载业务组件。

优点:

  • ① 方便获取组件内部状态,比如 state ,props ,生命周期,绑定的事件函数等。
  • ② es6继承可以良好继承静态属性。所以无须对静态属性和方法进行额外的处理。

缺点:

  • ① 函数组件无法使用。
  • ② 和被包装的组件耦合度高,需要知道被包装的原始组件的内部状态,具体做了些什么?
  • ③ 如果多个反向继承 HOC 嵌套在一起,当前状态会覆盖上一个状态。这样带来的隐患是非常大的,比如说有多个 componentDidMount ,当前 componentDidMount 会覆盖上一个 componentDidMount 。这样副作用串联起来,影响很大。

基本使用

接收一个组件作为参数,返回一个新组件

import React, { PureComponent } from 'react'
import Home from './Home'

// 定义高阶组件
function hoc(WrapperComponent) {
  // 1. 定义一个类组件
  class NewComponent extends PureComponent {
    render() {
      return <WrapperComponent></WrapperComponent>
    }
  }

  return NewComponent

  // 2. 定义一个函数组件
  // function  NewComponent2(props) {}

  // return NewComponent2
}

const HomeHoc = hoc(Home)

export class classHello extends PureComponent {
  render() {
    return (
      <div>
        <HomeHoc />
      </div>
    )
  }
}

export default classHello

Home.jsx

import React, { PureComponent } from 'react';

class Home extends PureComponent {
  render() {
    return (
      <div>
        122
      </div>
    );
  }
}

export default Home;

高阶组件应用

应用场景

基础使用

为什么React引入高阶组件的概念?它到底有何威力?让我们先通过一个简单的例子说明一下。

假设有一个组件MyComponent,需要从LocalStorage中获取数据,然后渲染数据到界面。我们可以这样写组件代码:

代码很简单,但当有其他组件也需要从LocalStorage中获取同样的数据展示出来时,需要在每个组件都重复componentWillMount中的代码,这显然是很冗余的。下面让我们

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白小白从不日白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值