说明与目录

 在学习本章内容之前,最好是具备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. Oracle ADDM报告生成和性能分析

    我写的SQL调优专栏:https://blog.csdn.net/u014427391/article/category/8679315 对于局部的,比如某个页面列表sql,我们可以使用Oracle的 ...

  2. ES 07 - Elasticsearch查询文档的六种方法

    目录 1 Query String Search(查询串检索) 2 Query DSL(ES特定语法检索) 3 Query Filter(过滤检索) 4 Full Text Search(全文检索) ...

  3. idea avtiviti 插件中文乱码

    idea 安转activiti插件后,编辑流程图发现保存后中文乱码,并且idea的字符集(Settings—>Editor—>File Encodings)已经设置为UTF-8,流程图中中 ...

  4. .NET Core 2.2 新增部分功能使用尝鲜

    前言     美国当地时间12月4日,微软2019开发者大会中发布了一系列的重磅消息,包含了软硬件和开源社区的各种好消息是铺天盖地,作为一名普通的开发者,我第一时间下载了 .NET Core 2.2 ...

  5. 浅谈SpringMVC执行过程

    通过深入分析Spring源码,我们知道Spring框架包括大致六大模块, 如Web模块,数据库访问技术模块,面向切面模块,基础设施模块,核心容器模块和模块, 其中,在Spring框架的Web模块中,又 ...

  6. 远程连接桌面报:这可能是由于credssp加密oracle修正

      1.Win+R 输入regedit打开注册表 找到对应的以下目录HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Polic ...

  7. MySQL InnoDB 存储引擎探秘

    在MySQL中InnoDB属于存储引擎层,并以插件的形式集成在数据库中.从MySQL5.5.8开始,InnoDB成为其默认的存储引擎.InnoDB存储引擎支持事务.其设计目标主要是面向OLTP的应用, ...

  8. Eclipse目录实解

    从左1图中可以看到,其中的src/main/java存放java文件,src/main/resources存放项目用到的资源(js,css,图片,文件等).下面的两个文件夹是用来存放测试文件和资源的( ...

  9. Java 处理 multipart/mixed 请求

    一.multipart/mixed 请求   multipart/mixed 和 multipart/form-date 都是多文件上传的格式.区别在于,multipart/form-data 是一种 ...

  10. Java工具类——通过配置XML验证Map

    Java工具类--通过配置XML验证Map 背景 在JavaWeb项目中,接收前端过来的参数时通常是使用我们的实体类进行接收的.但是呢,我们不能去决定已经搭建好的框架是怎么样的,在我接触的框架中有一种 ...