说明与目录

 在学习本章内容之前,最好是具备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. #Java学习之路——基础阶段二(第七篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  2. PHP——isset和empty

    前言 对于这两个PHP函数大家肯定都很熟悉,但是其二者的区别又有那些呢? 对比 isset  | 检测变量是否被设置过 1. 变量不存在,返回FALSE 2. 变量存在且其值为NULL,返回FALSE ...

  3. springboot结合maven打包发布

    本篇分享如何使用maven便利我们打springboot的发布包:我这里使用的是idea开发工具,首先创建了多个module的项目结构,如图: 要对多个module的项目做打包,一般情况都是在父级po ...

  4. expect实现自动交互由浅入深

    expect实现自动交互由浅入深 作为运维人员可以通过Shell可以实现简单的控制流功能,如:循环.判断等.但是对于需要交互的场合则必须通过人工来干预,有时候我们可能会需要实现和交互程序如telnet ...

  5. Spring:(一)入门基础学习

    前述 因为前几日刚刚学完Spring的基础,因此写一篇博客整理一下相关的基础知识. 什么是Spring? Spring 是一个轻量级的 DI / IoC 和 AOP 容器的开源框架,帮助分离项目组件之 ...

  6. 前端笔记之移动端&响应式(上)媒体查询&Bootstrap&动画库&zepto&velocity

    一.媒体(介)查询 1.1 基本语法 媒体查询由媒体类型和一个或多个检测媒体特性的条件表达式组成.媒体查询中可用于检测的媒体特性有:width.height和color(等).使用媒体查询可以在不改变 ...

  7. 从PRISM开始学WPF(番外)共享上下文 RegionContext-更新至Prism7.1

    RegionContext共享上下文 There are a lot of scenarios where you might want to share contextual information ...

  8. 还在问跨域?本文记录js跨域的多种实现实例

    前言 众所周知,受浏览器同源策略的影响,产生了跨域问题,那么我们应该如何实现跨域呢?本文记录几种跨域的简单实现 前期准备 为了方便测试,我们启动两个服务,10086(就是在这篇博客自动生成的项目,请戳 ...

  9. WIN10安装不上IIS,使用IISExpress作为发布服务

    [背景] 本人开发Win程序,需要调用网站资源作为Win程序的辅助功能,为此需要本地开发环境支持IIS.最近重装系统,VS安装完后,接着再安装IIS,可以在添加删除程序中反复尝试,均告安装失败提示.最 ...

  10. c#调用word文件

    大家好!我叫蓝颜,我是一名大专生.这是我第一次接触博客园,以后也会一直在. 在学校期间,参加技能大赛(物联网),接触到的C#.之后学校教务处要一个调课软件, 于是我就小试牛刀试了试.当然了,这也是我第 ...