1. 首页

带着三个问题深入浅出React高阶组件

前言

“高阶”二字听起来非常唬人,因为大学高数课上的高阶方程让人抓狂,从而让第一次接触”高阶组件”概念的人们误以为又是什么高深的思想和复杂的逻辑。但相信在你学习完成后和生产环境大量使用过程中,就会发现这个所谓”高阶组件”真的一点也不高阶,非常简单易懂。本文通过回答三个问题带你深入浅出 React 高阶组件。

1.为什么需要高阶组件?

2.高阶组件是什么?

3.如何实现高阶组件?

1.为什么需要高阶组件?

这个问题很简单,为什么我们需要 react/vue/angular?使用框架最核心的原因之一就是提高开发效率,能早点下班。同理,react 高阶组件能够让我们写出更易于维护的 react 代码,能再早点下班~

举个栗子,ES6 支持使用 import/export 的方式拆分代码功能和模块,避免一份文件里面出现”成坨”的代码。同理对于复杂的 react 组件,如果这个组件有几十个自定义的功能函数,自然要进行拆分,不然又成了”一坨”组件,那么该如何优雅地拆分组件呢?react 高阶组件应运而生。

在使用 ES5 编写 react 代码时,可以使用 Mixin 这一传统模式进行拆分。新版本的 react 全面支持 ES6 并提倡使用 ES6 编写 jsx,同时取消了 Mixin。因此高阶组件越来越受到开源社区的重视,例如 redux 等知名第三方库都大量使用了高阶组件。

2.高阶组件是什么?

回答这个问题前,我们先看下本文的封面图,为什么笔者用初中生就掌握的一元一次函数来代表这篇文章呢?很显然,高阶函数就是形如y=kx+b的东西,x是我们想要改造的原组件,y就是改造过后输出的组件。那具体是怎么改造的呢?kb就是改造的方法。这就是高阶组件的基本原理,是不是一点也不高阶~

再举个栗子相信更能让你明白:我们写代码需要进行加法计算,于是我们把加法计算的方法单独抽出来写成一个加法函数,这个加法函数可以在各处调用使用,从而减少了工作量和代码量。而我们独立出来的这个可以随处使用的加法函数,类比地放在 react 里,就是高阶组件。

3.如何实现高阶组件?

从上面的问题回答中,我们知道了:高阶组件其实就是处理 react 组件的函数。那么我们如何实现一个高阶组件?有两种方法:

1.属性代理 2.反向继承

第一种方法属性代理是最常见的实现方式,将被处理组件的props和新的props一起传递给新组件,代码如下:

//WrappedComponent为被处理组件
function HOC(WrappedComponent) {
  return class HOC extends Component {
    render() {
      const newProps = { type: 'HOC' };
      return (
        <div>
          <WrappedComponent {...this.props} {...newProps} />
        </div>
      );
    }
  };
}

@HOC
class OriginComponent extends Component {
  render() {
    return <div>这是原始组件</div>;
  }
}

//const newComponent = HOC(OriginComponent)

属性代理听起来好像很麻烦,然而从代码中看,就是使用 HOC 这个函数,向被处理的组件 WrappedComponent 上面添加一些属性,并返回一个包含原组件的新组件。从 chrome 调试台上我们可以看到原始组件已经被包裹起来了并具有type属性:

带着三个问题深入浅出React高阶组件

上述代码使用了 ES7 的 decorator 装饰器来实现对OriginComponent组件的装饰和增强,或者使用注释中的函数方法一样可以达到相同的效果。

使用属性代理的好处就是,可以把常用的方法独立出来并多次复用。比如我们实现了一个加法函数,那么我们把加法函数改造成形如上述HOC函数的形式,之后对其他组件进行包裹,就可以在组件里使用这个方法了。

第二种方法反向继承就有意思了,先看代码:

function HOC(WrappedComponent) {
  return class HOC extends WrappedComponent {
    //继承了传入的组件
    test1() {
      return this.test2() + 5;
    }

    componentDidMount() {
      console.log('1');
      this.setState({ number: 2 });
    }

    render() {
      //使用super调用传入组件的render方法
      return super.render();
    }
  };
}

@HOC
class OriginComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { number: 1 };
  }

  test2() {
    return 4;
  }
  componentDidMount() {
    console.log('2');
  }

  render() {
    return (
      <div>
        {this.state.number}
        {'and'}
        {this.test1()}
        这是原始组件
      </div>
    );
  }
}

//const newComponent = HOC(OriginComponent)

代码看完我们可能还有点懵,那我们先来剖析关键词”继承”。何谓继承?新生成的HOC组件通过extends关键字,获得了传入组件OriginComponent所有的属性和方法,是谓”继承”。也就是说继承之后,HOC组件能够实现OriginComponent组件的全部功能,而且,HOC可以拿到stateprops进行修改,从而改变组件的行为,也就是所谓的”渲染劫持”。可以说,通过反向继承方法实现的高阶组件相比于属性代理实现的高阶组件,功能更强大,个性化程度更高,适应更多的场景。

如上的代码,我们可以看到:

带着三个问题深入浅出React高阶组件

第一:this.test1()输出了 9。为什么? 因为在 ES6 中,super作为对象调用父类方法时,super绑定子类的this。故执行super.render()OriginComponent中的this指向的是HOC组件,所以能够成功地执行test1函数。

第二:控制台输出的是 1 而不是 2。为什么? 首先,decorator 是在代码编译阶段执行,故HOCrender方法在OriginComponentrender方法之前执行。并且子组件HOC是继承于父组件OriginComponent,两者具有继承关系HOC.__proto__ === OriginComponent,当执行componentDidMount方法时,子组件已存在该方法,故执行完毕后结束,不再根据__proto__向上继续寻找。如果我们将子组件HOC中的componentDidMount方法去掉,那么控制台将输出 2。

当我们有多个高阶组件需要同时增强一个组件时该怎么办呢?我们可以这样写:


@fun1 @fun2 @fun3 class OriginComponent extends Component { ... }

也可以使用lodashflowRight方法:


const enchance = lodash.flowRight(fun1,fun2,fun3); @enchance class OriginComponent extends Component { ... }

因为fun1 fun2 fun3都是处理类的函数,只要实现按顺序依次对类进行处理即可。

以上就是关于高阶组件实现方式的全部内容。为了查缺补漏,官方文档中有两条建议很中肯,在这里摘抄给大家:

带着三个问题深入浅出React高阶组件

一句话总结,为了避免在调试时,因为高阶组件的存在而导致满屏的HOC(以上述代码为例),可以设置类的displayName属性修改组件的名称。

带着三个问题深入浅出React高阶组件

如果你对 ES6 中的继承非常了解的话,那理解上述文字应该非常简单。ES6 的继承中,子类不能继承父类的静态方法,即使用static关键字定义的方法。如果子类想使用,那么一定要copy之后才能使用。

总结一下,高阶组件其实就是处理组件的函数,他有两种实现方式: 一是属性代理,类似于一元一次方程的y = x + b,输入x是原组件,参数b是你要添加或删除的属性/方法,y是最终输出的组件。 二是反向继承,类似于一元一次方程的y = kx + b,可以拿到stateprops进行渲染劫持(k),改变组件的行为。

作者:ssssyoki
链接:https://juejin.im/post/59818a485188255694568ff2

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:带着三个问题深入浅出React高阶组件

链接:https://www.javascriptc.com/3959.html

« 苹果完全屏蔽第三方 Cookie,七天清空本地存储
爱奇艺云剪辑 Web 端的技术实现»
Flutter 中文教程资源

相关推荐

QR code