说明与目录

 在学习本章内容之前,最好是具备react中‘插槽(children)’及‘组合与继承’ 这两点的知识积累。
详情请参照React 精要面试题讲解(四) 组合与继承不得不说的秘密。
哦不好意思忘记了,四还没写呢。==!回头补上。
__首先,我们要知道高阶组件能够做到什么: 对复用UI、数据逻辑等进行封装,对参数组件进行制式处理,从而让参数组建具备特定的ui或功能__
那么本节的学习目录:
  1. 高阶函数的认知

  2. 类的修饰器(decorator)的认知(类比高阶函数)

  3. 高阶组件的认知(类比修饰器)

  4. 高阶组件的两种形式(类比插槽)

  5. 高阶组件之组合(代理)类型的高阶组件

  6. 高阶组件之继承类型的高阶组件

  7. 高阶组件的认知总结

    必须深刻认知,以上内容是循序渐进且相互关联的关系,按照流程,我们将彻底把高阶组件安排的明明白白,玩个透彻。

1. 高阶函数的认知

在本系列之前的学习中,你应当明白——组件的本质是函数。
那么什么是高阶函数?
高阶函数也是一个函数,__它接受函数为参数,返回处理过后的函数或者一个新函数__。
那么我们知道,高阶函数的作用是对参数函数进行统一的加工处理。
形如:
    //这里我们写了一个函数作为之后的参数
//思考一下为什么把参数函数定义在上面
function testedFunc(){
this.a = 1;
} // 在此定义一个高阶函数
function highOrderFunc(func){
func.prototype.aaa = 3;
return func;
} //这里测试一下我们写的高阶函数
var newFunc = highOrderFunc(testedFunc); //打印看看处理后的函数原型及实例
console.log(newFunc.prototype,1);
console.log(new newFunc(),2);

(别那么懒,赶紧复制粘贴f12)

打印结果如下:



那么我们知道了,高阶函数 作用是 处理(加工)传入的函数,以达成某种目的…

2. 类的修饰器(decorator)的认知

ES6增添了class类,我们知道,类的本质也是函数。

     class testClass{
a = 1;
}
console.log( typeof testClass)
打印结果如下:

那么类的修饰器——decorator 是个怎样的东西咧?
__类的修饰器是es6的提案之一,在es7标准中实现。 修饰器也是一个函数,把传入的原有类修饰一下,return 出处理后的类或新类。__
这时候我们脑海中应该闪过一个词——高阶类…(???黑人问号)
不觉得太难听了吗?

ok,我们还是以代码来演示一下:

    //注意,下面这种@xx的写法是修饰器的标准写法,是属于es7的产物,要用babel哦~

    //定义一个修饰器decF
function decF(adornedClass){
return class extends adornedClass{
b= 2
}
} // 使用方式1 : @decF
@decF
class foo{
a = 1
}
console.log( new foo(),1) class bar{
a='bar'
}
// 使用方式2 : decF();
const newBar = decF(bar);
console.log( new newBar(),2);

打印如下:

瞧啊,修饰器就是这么个东西。

要注意的是,类的修饰器能否修饰函数?为什么?

// 可以自己去找答案,再讲别的就跑题了。


3. 高阶组件的认知(类比修饰器)

那么经过类的修饰器的认知,高阶组件的概念就很明朗了。

React高阶组件(high-order-component,简称hoc)就是一个类的修饰器啊…它接受一个组件类为参数,返回出一个新的组件类;

形如:

   // 用法1 高阶函数式写法
hoc(WrapedComponent);
// 用法2 decorator 修饰器写法
@hoc
class A extends React.Component{
//...
}

大家有木有很眼熟啊 ?

ok, 我们写一个常规的高阶组件并暴露出去;

   export default  WrapedComponent  => class  NewComponent extends React.Component{

         render(){
return <WrapedComponent a = '添加了一个属性' />
}
}

这种箭头函数的写法好理解吧。

如代码所示,我们写了一个高阶组件,返回的新组件 NewComponent里,用组合的形式使用了传入的组件WrapedComponent。(所以不明白组合与继承的童鞋,赶紧补一补再来看啊)

这里有人问 connect为啥两个括号啊

 //形如
connect(func1,func2 )( WrapedComponent)

OK ,我们也手写一下它。

     export default (func1,func2)=>WrapedComponent=>class NewComponent extends React.Component{
// ....
}

这个两层箭头函数好理解吧?

顺便说下,一般像connect这样多嵌套了一层的高阶函数,我称之为二阶高阶函数。此后类推,三个括号就叫三阶高阶函数…


4. 高阶组件的两种形式(类比插槽)

同children 一样,高阶组件也存在组合(也可称之为代理)和继承两种形式。

那么高阶组件和children插槽有什么关系呢?

我们来类比以下代码:

  //写一个组合形式的具备插槽功能的组件Provider
class Provider extends React.Component{
render(){
return (
<div>
{this.props.children}
</div>
)
}
}
// 下面是使用的方式
import ComA from './ComA'
const useB = (<Provider > <ComA a='使用Provider时添加了a属性'/> </Provider >)
   // 写一个组合形式的高阶组件
const hocAddA = WrapedComponent => class NewComponent extends React.component{
render(){
return (
<div>
<WrapedComponent a ='定义hocAddA时添加了a属性'/>
</div>
)
}
}
// 我们来尝试使用它
const NewA = hocAddA(ComA);
// 或者
@hocAddA
class ComB ...

ok, 上述两种代码在使用后的表现几乎是一致的(同样实现了给ComA添加属性a的功能)。

但是注意,插槽(children)的实现,不关心插槽组件的功能变化。只是把插槽当作当前组件的子组件去使用(这就是组合)。

   //同样的,我现在这样使用
import ComA from './ComA'
const useB = (<Provider > <ComA b='使用Provider时添加了b属性'/> </Provider >)

而高阶函数,在定义时就写死了参数组件的功能变化。

传入组件,得出的组件只会添加属性a。

当然,我们也可以通过二阶高阶函数实现 用参数控制参数组件的功能变化:

  定义一个二阶高阶组件
const hocAddProps = props => WrapedComponent => class NewComponent extends React.Component{
render(){
return (
<div>
<WrapedComponent {...props}/>
</div>
)
}
}
// 于是我们这样使用它
const propsAdded = {
a: '添加了一个属性a',
b: ‘添加了一个属性b'
}
const NewA = hocAddProps(propsAdded)(ComA)

诸如此类。实际上组合形式的高阶组件能做到的事,用children基本都能做到。

那么组合形式的高阶组件和继承形式的高阶组件的区别在哪呢?

组合形式(也称之为代理形式): 返回的新组件,继承的还是React.Component,只是把参数组件作为新组件的子组件去使用,能够实现给参数组件进行包装、属性的增删改、状态抽离等功能.

继承形式: 返回的新组件,继承的是 参数组件 ,从而实现以参数组件为模版,改写参数组件的功能。

上述划重点,要考。

我们再回过头来思考类的修饰器——返回一个新的类或改写参数类。

是不是一样的道理啊。

所以说高阶组件啥的,还是js啊,最多加了jsx的语法嘛。

5. 高阶组件之组合(代理)类型的高阶组件

上述我们已经知道了组合(代理)类型的高阶组件的概念和思想,以及它能实现的功能。

那么我们上demo代码


import React,{Component,createRef} from 'react';
export default title=>WrapedComponent=> class NewComponent extends Component{
//抽离状态
state={
value:''
}
// 访问refs
myref=createRef();
handleInputChange=(e)=>{
this.setState({
value:e.target.value
})
} render(){
const {wap,...otherprops} = this.props;
const newProps = {
value:this.state.value,
onChange:this.handleInputChange
}
//包装组件
return (
<div>
我是组件NewComponent,是典型的代理形式的高阶组件,我除了做自己的事,还可以对 我的参数组件:
1增加/删减props 2抽离状态 3访问ref 4包装组件
<div>我的title:{title}</div>
<WrapedComponent {...otherprops} ref={this.myref} inputProps={newProps}/>
</div>
)
}
}

这里要单独说一下上述功能中的状态抽离。

状态抽离(状态提升): 把参数组件(即代理形式中使用的子组件)的状态提升到NewComponent(即代理形式中的当前组件,也就是父组件) 中,这样一来,子组件只负责UI渲染,而父组件通过props传递state实现数据的控制

也就是说, NewComponent 成为参数组件的容器组件,参数组建单纯作为UI组件

ps: 容器组件和UI组件的概念是相对的。 例如 把B的状态抽离到父组件A上,那么A相对于B来说是B的容器组件,要这么去理解。后续讲react-redux中会提到。


6. 高阶组件之继承类型的高阶组件

同样的,上述我们已经知道了 继承类型的高阶组件的概念和思想,那么我们也直接上demo代码

import React from  'react'

//这个是给返回的新组件起名用的函数,有兴趣可以结合调试器玩玩。
function getDisplayName(WrapedComponent){
return WrapedComponent.displayName||WrapedComponent.name||'component'
} export default color=>WrapedComponent=> class NewComponent extends WrapedComponent{
// static displayName = `E(${getDisplayName(Inconponent)})`
;
aaa = '我改写了参数组件中的aaa属性'
compoenentDidMount(){
console.log('我不仅可以改写属性和方法,我还能改写钩子')
}
render(){
const {wap,...otherprops} = this.props;
const element = super.render();
console.log(element);
const newStyle = {
color:element.type==='div'?color:null
}
const newProps = {
...otherprops,
style:newStyle
}
// 我甚至还改写了参数组件的UI
return React.cloneElement(element,newProps,element.props.children)
}
}

如上述代码所示(跟着敲一下啊懒虫),我们成功做到了以参数组件为模版,改写了参数组件中已定义的属性、方法、钩子,甚至UI,增添了参数组件中未定义的属性、方法、钩子等。

当然,同官方文档中 ‘组合和继承’ 这一章中的思想一致,绝大部分情况下,我们用不到继承类型的高阶组件,也不提倡这种形式的用法(其实我个人觉得挺好玩的)。


7. 高阶组件的认知总结

那么我们通过以上学习,已经完完整整掌握了高阶组件的使用。
在日常项目中,我们也可以在合适的场景中使用高阶组件完成对应的需求。
回顾最上面提到过的高阶组件的使用场景:
__对复用UI、数据逻辑等进行封装,对参数组件进行制式处理,从而让参数组建具备特定的ui或功能__ 再回顾下上述讲到过的高阶函数,类的修饰器等——
你get到了吗? 面试中会问到高阶组件的问题,消化掉这一篇,那么你便可以连续不断的给面试官讲上半个小时征服他。

最后,如果本章内容对你的react学习有帮助,记得点个关注,等待更新哦。

React 精要面试题讲解(五) 高阶组件真解的更多相关文章

  1. React 精要面试题讲解(一) 单向数据流

    react 单向数据流概念 'react框架是怎样的数据流向?'||'react单向数据流是怎样的概念 ?' 解答这个问题之前,我们首先得知道,js框架是个怎样的概念. 框架:具备一定**编程思想** ...

  2. React躬行记(10)——高阶组件

    高阶组件(High Order Component,简称HOC)不是一个真的组件,而是一个没有副作用的纯函数,以组件作为参数,返回一个功能增强的新组件,在很多第三方库(例如Redux.Relay等)中 ...

  3. React 精要面试题讲解(二) 组件间通信详解

    单向数据流与组件间通信 上文我们已经讲述过,react 单向数据流的原理和简单模拟实现.结合上文中的代码,我们来进行这节面试题的讲解: react中的组件间通信. 那么,首先我们把看上文中的原生js代 ...

  4. 聊聊React高阶组件(Higher-Order Components)

    使用 react已经有不短的时间了,最近看到关于 react高阶组件的一篇文章,看了之后顿时眼前一亮,对于我这种还在新手村晃荡.一切朝着打怪升级看齐的小喽啰来说,像这种难度不是太高同时门槛也不是那么低 ...

  5. React文档(二十四)高阶组件

    高阶组件(HOC)是React里的高级技术为了应对重用组件的逻辑.HOCs本质上不是React API的一部分.它是从React的组合性质中显露出来的模式. 具体来说,一个高阶组件就是一个获取一个组件 ...

  6. 函数式编程与React高阶组件

    相信不少看过一些框架或者是类库的人都有印象,一个函数叫什么creator或者是什么什么createToFuntion,总是接收一个函数,来返回另一个函数.这是一个高阶函数,它可以接收函数可以当参数,也 ...

  7. React中的高阶组件

    高阶组件(HOC, High-Order Component)是React中用于重组组件逻辑的高级技术,是一种编程模式而不是React的api. 直观来讲,高阶组件是以某一组件作为参数返回一个新组件的 ...

  8. React——高阶组件

    1.在React中higher-order component (HOC)是一种重用组件逻辑的高级技术.HOC不是React API中的一部分.HOC是一个函数,该函数接收一个组件并且返回一个新组件. ...

  9. react 高阶组件的 理解和应用

    高阶组件是什么东西 简单的理解是:一个包装了另一个基础组件的组件.(相对高阶组件来说,我习惯把被包装的组件称为基础组件) 注意:这里说的是包装,可以理解成包裹和组装: 具体的是高阶组件的两种形式吧: ...

随机推荐

  1. Python用Django写restful api接口

    用Python如何写一个接口呢,首先得要有数据,可以用我们在网站上爬的数据,在上一篇文章中写了如何用Python爬虫,有兴趣的可以看看: https://www.cnblogs.com/sixrain ...

  2. Windows10系统:任务栏中电池图标消失问题的解决方法

    一.问题: 电池图标对我们来说,基本是没有什么操作对它进行的,但这并不代表它不重要. 持续充电的时候自然觉得没什么,但一旦电脑脱离充电状态,我们还是会经常性地把鼠标放在电池图标上.只有清楚地了解电脑的 ...

  3. ES 06 - 通过Kibana插件增删改查ES中的索引文档

    目录 1 document的结构 2 document的常见CRUD操作 2.1 添加商品: 添加文档并建立索引 2.2 查询商品: 检索文档 2.3 修改商品: 替换文档 2.4 修改商品: 更新文 ...

  4. 机器学习——logistic回归,鸢尾花数据集预测,数据可视化

    0.鸢尾花数据集 鸢尾花数据集作为入门经典数据集.Iris数据集是常用的分类实验数据集,由Fisher, 1936收集整理.Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集.数据集包含150个数 ...

  5. Python的魔法函数系列 __getattrbute__和__getattr__

      #!/usr/bin/env python # -*- coding: utf-8 -*- import sys __metaclass__ = type """ _ ...

  6. [macOS开发.NET Core] 一个简单的WEB程序

    上一篇咱们提到了在macOS下选进行开发. 咱们已经把工具准备完成了. 现在咱们做一个简单的DEMO 创建WEB程序  之前咱们已经创建过WEB程序,并且成功的运行过数据 现在咱们创建一个页面,并显 ...

  7. PHPStorm中对nodejs项目进行单元测试

    安装必要的包 nodejs的单元测试最常用的是使用mocha包.首先确保你本地安装nodejs,之后安装mocha包. npm install mocha -g 然后还需要安装相关的断言工具,Node ...

  8. 【带着canvas去流浪】(2)绘制折线图

    目录 一. 任务说明 二. 重点提示 三. 示例代码 3.1 一般折线图 3.2 用贝塞尔曲线绘制平滑折线图 四. 大数据量场景 示例代码托管在:https://github.com/dashnowo ...

  9. C#复制文件全代码--供参考

    private void button1_Click(object sender, EventArgs e) { //创建文件对象 FileInfo fi = null; //实例化打开文件对话框 O ...

  10. .net工具类 获取枚举类型的描述

    一般情况我们会用枚举类型来存储一些状态信息,而这些信息有时候需要在前端展示,所以需要展示中文注释描述. 为了方便获取这些信息,就封装了一个枚举扩展类. /// <summary> /// ...