Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

  • Webpack

  • Redux

  • React源码

  • 组件库

  • React高阶系列

    • 高阶组件基础
      • 0. 前言
      • 1. 什么是高阶组件?
      • 2. 怎么写 HOC ?
        • 入参组件的 Typescript
        • 出参组件分情况讨论
      • 3.命名规范
      • 4.小结
    • React 组件复用
  • UMI插件

  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • React高阶系列
wangjiasheng
2022-10-09
目录

高阶组件基础

# 0. 前言

本篇博客主要就是讲解 React 高阶组件 - HOC。

不会讲故事,就直接开始讲解 HOC 的梳理工作。

本篇博客的项目演示地址:https://wangjs-jacky.github.io/jacky-workspace-html/react18/hoc (opens new window)

# 1. 什么是高阶组件?

高阶组件HOC ,英文全称为 Heigher Order Component ,用下面一张图就可以阐述清楚

由上图可知,高阶组件就是入参出参皆为 Component 。

# 2. 怎么写 HOC ?

高阶组件虽然简单,但是由于在 React 框架中存在两种类型的组件,即Class Component 以及 Function Component ,因此在实现层面上有多种写法。

# 入参组件的 Typescript

在封装 HOC 的入参,难点在于 Typescript 的定义,由于不知道接受的是 Class Component 还是 Function Component ,所以需要一个联合类型去涵盖这两个情况,在 React 已经内置这个类型 ComponentType<P> 。

type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
1

举例如下:

const WithComponent = (Component) => {
  return (props): JSX.Element => {
    return (
      <div>
        <header>this is header</header>
        <Component {...props} />
      </div>
    );
  };
};
1
2
3
4
5
6
7
8
9
10

在上述代码中,我们需要补全两个部分的 ts 类型定义。

  • 其一、是入参 Compnent 的类型。
  • 其二、是props 的类型。

/* type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>; */
const CreateWithComponent = (title: string) => {
  /* 返回一个 HOC(即,入参组件,出参也是组件) */
  /* 难点:入参组件既有可能是一个 Class 也可能是 Function Component */
  return <P extends {}>(Component: React.ComponentType<P>) => {
    return (props: P): JSX.Element => {
      return (
        <div style={{ border: '1px solid #000' }}>
          <h2>接受的参数是:{title}</h2>
          <Component {...props} />
        </div>
      );
    };
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

在定义返回的组件类型时,就需要确定组件接受需要的 props 的类型,因为这部分的参数类型需要等到 props 传递时才能确定,因此在定义的时候应预留出 泛型坑位。

整个函数接受入参的逻辑为:(title)=>(Component<P>)=>(props:P)=>JSX.Element

# 出参组件分情况讨论

同上,出参情况也分 Class Component 还是 Function Component 。

# 情况一:Function Component

通过一个简单案例来了解具体写法,下面这个 HOC 可添加一个标题。

步骤如下:

  1. 构造 CreateWithComponent,依次接受两个参数。

    结构:(title)=>(Component)=>Function Component,其中 Component=>Function Component 为 HOC。

    const CreateWithComponent = (title: string) => {
      return <P extends {}>(Component: React.ComponentType<P>) => {
        return (props: P): JSX.Element => {
          return (
            <div style={{ border: '1px solid #000' }}>
              <h2>接受的参数是:{title}</h2>
      				/* HOC 功能:属性继承  */
              <Component {...props} />
            </div>
          );
        };
      };
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  2. 使用 CreateWithComponent 创造具有一个相同 title 的 HOC

    const WithComponent = CreateWithComponent('child1');
    
    1
  3. 使用 HOC 生成可供消费组件

    const Wrapper = WithComponent(ComponentA);
    
    1
  4. 将 Function Componet 渲染到页面。

    有两种渲染方式,见下:

    const Main = () => {
      /* 此组件可以传递参数 */
      return (
        <>
          <Wrapper attr="使用方案1"></Wrapper>
          <br></br>
          {WithComponent(ComponentA)({ attr: '使用方案2' })}
        </>
      );
    };
    export default Main;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 情况二:Class Component

当输出组件为类组件时,又存在两种继承方式。

  • 继承自 React.Component

    return class WithComponent extends React.Component<P, {}> {}
    
    1
  • 【反向继承】当入参组件为 Class Component,可直接通过继承该组件实现 HOC

    return class WithComponent extends Component{}
    
    1

    这种继承方式,也被称为 反向继承。

    为了保持文章的逻辑性,此部分内容将另起一篇博客叙述,本篇设定接受的组件既有可能是Class 组件也可能是 Function 组件的情况。

第一种继承方式的 HOC 编写,步骤如下:

  1. 构造 CreateWithComponent,依次接受两个参数。

    结构:(title)=>(Component)=>Class Component,其中 Component=>Class Component 为 HOC。

    const CreateWithComponent = (title: string) => {
      return <P extends {}>(Component: React.ComponentType<P>) => {
        /* 继承自 React.Component */    
        return class WithComponent extends React.Component<P, {}> {
          render() {
            return (
              <div style={{ border: '1px solid #000' }}>
                <h2>当前组件是:{title}</h2>
                <Component {...this.props} />
              </div>
            );
          }
        };
      };
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  2. 使用 CreateWrapper 创造具有一个相同 title 的 HOC

    const WithComponent = CreateWithComponent('child2');
    
    1
  3. 使用 HOC 生成可供消费组件

    const Wrapper = CreateWithComponent(ComponentA);
    
    1
  4. 将 Class Componet 渲染到页面。

    同样有两种渲染方式,注意 Class Componet 的渲染需要实例化后,调用 render 函数获取 JSX.ELEMENT

    const Main = () => {
      /* 此组件可以传递参数 */
      const Element = new Wrapper({ attr: '使用方法2' }).render();
      return (
        <>
          <Wrapper attr="使用方案1"></Wrapper>
          <br></br>
          {Element}
        </>
      );
    };
    export default Main;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 3.命名规范

高阶组件的命名,推荐使用 With + 组件名称的写法,如 withFromTable,withRouter 等。

对于HOC 为匿名函数的情况,推荐使用: Create + With + 组件名称的写法,如 CreateWithFormTable,CreateWithRouter 等。

# 4.小结

本篇博客主要内容如下:

  • 使用 React.ComponentTyp 定义处理接受的组件既可以是类组件也可以是函数组件。

  • 当 HOC 返回的是一个 Function Component 时。

    除了传统的 <Compoent /> ,还可以通过 WithComponent(Component)(props) 获取 JSX.Element 并渲染在页面上。

  • 当 HOC 返回的是一个 Class Component 时。

    需要使用 反向继承 的方式对组件进行 Wrap 包裹。

    并且也提供了两种渲染方式,除了传统的 <Compoent /> ,还可以通过构建实例对象,并调用 render 函数的方式获取 JSX.Element 并渲染在页面上。

编辑 (opens new window)
上次更新: 2022/10/28, 0:10:00
React 组件:Loading 弹窗
React 组件复用

← React 组件:Loading 弹窗 React 组件复用→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式