# React 中高阶函数与高阶组件

# 快速导航

# 前言

React 中最大的一亮点,就是组件化开发模式,而编写 React 组件,对于无状态的组件,我们可以用函数式组件编写,而复杂的组件(聪明组件/外层组件)可以用类class编写组件

在 React 中提到了高阶函数与高阶组件,一直以来,对它们都是持以仰望的,逼格高的词调,常常把自己给整晕的,做个记录总结一下的

# 什么是高阶函数?

# 函数可以被作为参数传递

如下:经常用到的setTimeout,setInterval

# 函数作为返回值输出

一个函数可以有返回值,也可以无返回值,若无指定返回值,它会默认返回undefined

函数是对象,这意味着函数可以存储在一个变量,数组,或对象中,同时函数可以传递给函数,并由函数返回,它可以拥有属性,也可以是一个值,可以像JavaScript中的其他表达式那样被当做参数一个传递

function foo(x) {
  return function() {
    return x;
  };
}
1
2
3
4
5

上面的 foo 函数接收的形参是x,函数 foo 的返回值是一个匿名函数,匿名函数返回值返回形参x

那么此时foo函数就是以函数作为返回值作为输出的高阶函数

# 高阶函数应用

# 高阶组件

概念: 高阶组件就是接收一个组件作为参数并返回一个新组件的函数

说明: 高阶组件是一个函数,并不是组件

例如:如下面的弹出框

提示
A
B
C
D
E
提示
A
B
C
D

经过 UI,可以将上面的公共的部分以及不同的部分给提取出来,封装成三个组件,分别为组件 A(公共部分),组件 B,组件 C

提示
A
B
C
D
E
A
B
C
D

可以用create-react-app脚手架工具创建一个项目,在src目录下创建一个components文件夹,这个文件主要用于存放我们的自定义组件

components中创建一个highcomponent,同时在该文件夹内创建ComponentA.js,ComponentB.js,ComponentC.js

经过上面的代码编写:达到了组件复用的目的

高阶组件

# 为什么需要高阶组件

多个组件都需要某个相同的功能,使用高阶组件减少重复实现

react-redux 中的connect连接器就是一个高阶组件

export default connect(mapStateToProps, mapDispatchToProps)(Header);
1

# 高阶组件的实现

⒈ 如何编写高阶组件

⒉ 如何使用高阶组件

⒊ 如在高阶组件中实现传递参数

# 如何编写高阶组件

⒈ 实现一个普通组件

⒉ 将一个普通组件使用函数包裹

# 如何使用高阶组件

⒈ higherOrderComponent(WrappedComponent)

⒉ @highOrderComponent

假设现在我编写了一个组件compnentF.js组件,将该组件当做参数传递给组件 componentD,然后渲染到页面上

以下为第一种使用方式,函数调用方式

使用高阶组件,还有另外一种方式,就是使用装饰器方式,即@+函数名,它是一个语法糖,简化了我们的写法

# 方式 1-安装 babel 插件在 babelrc 中配置

在使用这种装饰器方式时,需要对create-react-app做一些配置,它默认是不支持装饰器模式的,你需要对项目做一些配置

create-react-app根目录中终端下使用npm run eject,这条命令主要是将我们的配置项做一个反向输出,暴露出隐藏的 webpack 配置项,这样可以项目进行修改了的,注意它是不可逆的

使用装饰器模式时:需要安装两个依赖:

cnpm install -D babel-preset-stage-2
cnpm install -D babel-preset-react-native-stage-0
1
2

然后你需要在根目录下创建一个.babelrc文件,对.babelrc文件做一些配置

{
  "presets": ["react-native-stage-0/decorator-support"]
}
1
2
3

经过这么配置后,就可以使用装饰器了的

import React, { Component } from 'react';
import componentD from './componentD'; // 引入函数componentD高阶组件

@componentD // 直接用@符号+高阶组件componentD就可以了的
class componentF extends Component {
  render() {
    return <div>我是组件F</div>;
  }
}

export default componentF;
1
2
3
4
5
6
7
8
9
10
11

# 方式 2-经过 eject 后在 package.json 中的 plugins 中配置

当用ejectwebpack一些配置弹射出来以后,会看到根目录下的package.json文件下新增了很多文件

babel对象处进行插件的配置,将@babel/plugin-proposal-decorators添加到plugins

{
  "babel": {
    "presets": [
      "react-app"
    ],

    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            { "legacy": true }
        ]
    ]

  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

经过这么配置后就可以使用装饰模式模式了的-即@+函数名简写的方式

为什么需要 eject 暴露呢

因为默认create-react-app虽然已经内已经安装了 @babel/plugin-proposal-decorators插件,但是需要自己进行配置

若不进行配置,它是不支持装饰器模式的

# React 中高阶组件的应用

# 代理方式的高阶组件

通过被包裹的React组件来操作props

返回的新组件直接继承自React.Component类,新组件扮演的角色传入参数组件的一个代理,在新组件的 render 函数中,将被包裹组件渲染出来,除了高阶组件自己要做的工作,其余功能全都转手给了被包裹的组件

应用场景

总得来说代理方式的高阶组件形式如下所示

export default () => (wrappedComponent) =>
  class A extends Component {
    render() {
      const { ...otherProps } = this.props;
      return <WrappendComponent {...otherProps} />;
    }
  };
1
2
3
4
5
6
7

# 继承方式的高阶组件

继承于被包裹的React组件

采用继承关联作为参数的组件和返回的组件,加入传入的参数wrappedComponent,那么返回的组件直接继承自wrappedComponent

export default () => (wrappedComponent) =>
  class A extends wrappedComponent {
    render() {
      const { user, ...otherProps } = this.props;
      this.props = otherProps;
      return super.render();
    }
  };
1
2
3
4
5
6
7
8

继承方式的高阶组件,继承方式是参数 wrappedComponent,而代理的高阶组件直接是 component,返回的结果也不同,代理高阶组件的返回值是参数的返回值,而继承方式是直接返回一个super.render

⒈ 操作 props

如下是componentH继承方式组件,定义了两个组价componentIcomponentJ

⒉ 操纵生命周期函数

继承方式的高阶组件需要修改生命周期函数直接在高阶组件内重写生命周期函数就可以了的,它会覆盖掉参数组件的生命周期函数

结论

使用代理方式的高阶组件要优于继承方式的高阶组件,所以应优先使用代理方式的高阶组件

它们的一个主要区别是:代理式高阶组件是继承自React.component而继承式高阶组件是继承自被包裹的React组件

# 如何显示高阶组件名

显示高阶组件名,是为了更好的 debug 调试,如果没有进行设置,只能通过查看源代码的方式

无法在浏览器中非常的直观看到

# 结语

本节主要讲述了 React 中的高阶函数以及高阶组件的使用,所谓高阶函数就是一个函数可以被当做参数传递,返回值也可以是函数作为输出

而高阶组件,是以接收一个组件作为参数并返回一个新的组件(类)的函数,并有代理式高阶组件,继承式高阶组件

以及装饰器的使用,显示高阶组件名称等

如果您有对 React 中高阶组件以及高阶函数有疑问,欢迎下方留言,一起讨论

关注公众号

一个走心,有温度的号,同千万同行一起交流学习

加作者微信

扫二维码 备注 【加群】

扫码易购

福利推荐