React学习笔记——高阶组件

161 阅读3分钟

高阶组件能解决什么问题

HOC的产生根本作用就是解决大量的代码复用,逻辑复用问题。既然说到了逻辑复用,那么具体复用了哪些逻辑呢?

  • 组件权限问题,拦截加载、动态加载、懒加载等问题;
  • 如果不想改变组件,只是监控组件的内部状态,对组件做一些赋能,HOC 也是一个不错的选择;

高阶组件的基本概念

高阶组件真的很好理解,都知道高阶函数就是一个将函数作为参数并且返回值也是函数的函数。高阶组件是以组件作为参数,返回组件的函数。返回的组件把传进去的组件进行功能强化。

image.png

常用的高阶组件有属性代理反向继承两种,

属性代理

属性代理,就是用组件包裹一层代理组件,在代理组件上,可以做一些,对源组件的强化操作。这里注意属性代理返回的是一个新组件,被包裹的原始组件,将在新的组件里被挂载。

function HOC(WrapComponent){
    return class Advance extends React.Component{
       state={
           name:'alien'
       }
       render(){
           return <WrapComponent  { ...this.props } { ...this.state }  />
       }
    }
}

优点:

  • ① 属性代理可以和业务组件低耦合,零耦合,对于条件渲染和 props 属性增强,只负责控制子组件渲染和传递额外的 props 就可以了,所以无须知道,业务组件做了些什么。
  • ② 同样适用于类组件和函数组件。
  • ④ 可以嵌套使用,多个 HOC 是可以嵌套使用的,而且一般不会限制包装 HOC 的先后顺序。

缺点:

  • ① 一般无法直接获取原始组件的状态,如果想要获取,需要 ref 获取组件实例。
  • ② 无法直接继承静态属性。如果需要继承需要手动处理,或者引入第三方库。
  • ③ 因为本质上是产生了一个新组件,所以需要配合 forwardRef 来转发 ref。

反向继承

反向继承和属性代理有一定的区别,在于包装后的组件继承了原始组件本身,所以此时无须再去挂载业务组件。

class Index extends React.Component{
  render(){
    return <div> hello,world  </div>
  }
}
function HOC(Component){
    return class wrapComponent extends Component{ /* 直接继承需要包装的组件 */
        
    }
}
export default HOC(Index) 

优点:

  • ① 方便获取组件内部状态,比如 state ,props ,生命周期,绑定的事件函数等。
  • ② es6继承可以良好继承静态属性。所以无须对静态属性和方法进行额外的处理。

缺点:

  • ① 函数组件无法使用。
  • ② 和被包装的组件耦合度高,需要知道被包装的原始组件的内部状态,具体做了些什么?
  • ③ 如果多个反向继承 HOC 嵌套在一起,当前状态会覆盖上一个状态。

如何编写高阶组件

  1. 强化props
  2. 控制渲染
const HOC = (WrapComponent) =>
  class Index  extends WrapComponent {
    render() {
      if (this.props.visible) {
        return super.render()
      } else {
        return <div>暂无数据</div>
      }
    }
  }
  1. 组件赋能(ref获取实例,事件监控)

具体使用方式:

对于 class 声明的类组件,可以用装饰器模式,对类组件进行包装:

@HOC1(styles)
@HOC2
@HOC3
class Index extends React.Componen{
    /* ... */
}

对于函数组件:

function Index(){
    /* .... */
}
export default HOC1(styles)(HOC2( HOC3(Index) )) 

HOC1 -> HOC2 -> HOC3 -> Index

总结

下面对 HOC 具体能实现那些功能,和如何编写做一下总结:

  • 1 强化 props ,可以通过 HOC ,向原始组件混入一些状态。
  • 2 渲染劫持,可以利用 HOC ,动态挂载原始组件,还可以先获取原始组件的渲染树,进行可控性修改。
  • 3 可以配合 import 等 api ,实现动态加载组件,实现代码分割,加入 loading 效果。
  • 4 可以通过 ref 来获取原始组件实例,操作实例下的属性和方法。
  • 5 可以对原始组件做一些事件监听,错误监控等。

注意事项:

  1. 谨慎修改原型链
  2. 不要在函数组件内部或类组件render函数中使用HOC
  3. ref的处理(forwardRef)