react高阶组件

2018-03-21  本文已影响63人  流动码文

React进阶之高阶组件

前言

本文代码浅显易懂,思想深入实用。此属于react进阶用法,如果你还不了解react,建议从文档开始看起。

我们都知道高阶函数是什么, 高阶组件其实是差不多的用法,只不过传入的参数变成了react组件,并返回一个新的组件.

A higher-order component is a function that takes a component and returns a new component.

形如:

const EnhancedComponent = higherOrderComponent(WrappedComponent);

高阶组件是react应用中很重要的一部分,最大的特点就是重用组件逻辑。它并不是由React API定义出来的功能,而是由React的组合特性衍生出来的一种设计模式。如果你用过redux,那你就一定接触过高阶组件,因为react-redux中的connect就是一个高阶组件。

原文https://github.com/sunyongjian/blog/issues/25
欢迎star
另外本次demo代码都放在 https://github.com/sunyongjian/hoc-demo
clone下来跑一下加深理解

引入

先来一个最简单的高阶组件

import React, { Component } from 'react';
import simpleHoc from './simple-hoc';

class Usual extends Component {
  render() {
    console.log(this.props, 'props');
    return (
      <div>
        Usual
      </div>
    )
  }
}
export default simpleHoc(Usual);
import React, { Component } from 'react';

const simpleHoc = WrappedComponent => {
  console.log('simpleHoc');
  return class extends Component {
    render() {
      return <WrappedComponent {...this.props}/>
    }
  }
}
export default simpleHoc;

组件Usual通过simpleHoc的包装,打了一个log... 那么形如simpleHoc就是一个高阶组件了,通过接收一个组件class Usual,并返回一个组件class。 其实我们可以看到,在这个函数里,我们可以做很多操作。 而且return的组件同样有自己的生命周期,function,另外,我们看到也可以把props传给WrappedComponent(被包装的组件)。 高阶组件的定义我都是用箭头函数去写的,如有不适请参照arrow function

装饰器模式

高阶组件可以看做是装饰器模式(Decorator Pattern)在React的实现。即允许向一个现有的对象添加新的功能,同时又不改变其结构,属于包装模式(Wrapper Pattern)的一种

ES7中添加了一个decorator的属性,使用@符表示,可以更精简的书写。那上面的例子就可以改成:

import React, { Component } from 'react';
import simpleHoc from './simple-hoc';

@simpleHoc
export default class Usual extends Component {
  render() {
    return (
      <div>
        Usual
      </div>
    )
  }
}

是同样的效果。
当然兼容性是存在问题的,通常都是通过babel去编译的。 babel提供了plugin,高阶组件用的是类装饰器,所以用transform-decorators-legacy babel

两种形式

属性代理

引入里我们写的最简单的形式,就是属性代理(Props Proxy)的形式。通过hoc包装wrappedComponent,也就是例子中的Usual,本来传给Usual的props,都在hoc中接受到了,也就是props proxy。 由此我们可以做一些操作

反向继承

反向继承(Inheritance Inversion),简称II,本来我是叫继承反转的...因为有个模式叫控制反转嘛...
跟属性代理的方式不同的是,II采用通过 去继承WrappedComponent,本来是一种嵌套的关系,结果II返回的组件却继承了WrappedComponent,这看起来是一种反转的关系。
通过继承WrappedComponent,除了一些静态方法,包括生命周期,state,各种function,我们都可以得到。上栗子:

 // usual
import React, { Component } from 'react';
import iiHoc from './ii-hoc';

@iiHoc
export default class Usual extends Component {

  constructor() {
    super();
    this.state = {
      usual: 'usual',
    }
  }

  componentDidMount() {
    console.log('didMount')
  }

  render() {
    return (
      <div>
        Usual
      </div>
    )
  }
}
//IIHOC
import React from 'react';

const iiHoc = WrappedComponent => class extends WrappedComponent {
    render() {
      console.log(this.state, 'state');
      return super.render();
    }
}

export default iiHoc;

iiHoc return的组件通过继承,拥有了Usual的生命周期及属性,所以didMount会打印,state也通过constructor执行,得到state.usual。
其实,你还可以通过II:

渲染劫持

这里HOC里定义的组件继承了WrappedComponent的render(渲染),我们可以以此进行hijack(劫持),也就是控制它的render函数。栗子:

  //hijack-hoc
  import React from 'react';

  const hijackRenderHoc = config => WrappedComponent => class extends WrappedComponent {
    render() {
      const { style = {} } = config;
      const elementsTree = super.render();
      console.log(elementsTree, 'elementsTree');
      if (config.type === 'add-style') {
        return <div style={{...style}}>
          {elementsTree}
        </div>;
      }
      return elementsTree;
    }
  };

  export default hijackRenderHoc;
  //usual
  @hijackRenderHoc({type: 'add-style', style: { color: 'red'}})
  class Usual extends Component {
    ...
  }

我这里通过二阶函数,把config参数预制进HOC, 算是一种柯理化的思想。
栗子很简单,这个hoc就是添加样式的功能。但是它暴露出来的信息却不少。首先我们可以通过config参数进行逻辑判断,有条件的渲染,当然这个参数的作用很多,react-redux中的connect不就是传入了props-key 嘛。再就是我们还可以拿到WrappedComponent的元素树,可以进行修改操作。最后就是我们通过div包裹,设置了style。但其实具体如何操作还是根据业务逻辑去处理的...

[图片上传中...(image-936804-1521617092866-1)]

我的应用场景

注意点(约束)

其实官网有很多,简单介绍一下。

总结

高阶组件最大的好处就是解耦和灵活性,在react的开发中还是很有用的。
当然这不可能是高阶组件的全部用法。掌握了它的一些技巧,还有一些限制,你可以结合你的应用场景,发散思维,尝试一些不同的用法。

上一篇下一篇

猜你喜欢

热点阅读