什么是 React 高阶组件

144 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

核心描述

  • 定义:高阶组件本质就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。高阶组件是 React 中的高级技术,用来实现重用组件逻辑。HOC 并不是 React 的 API,可以看做是 React 对装饰模式的一种实现。
  • 实际应用:日志打点,可用、权限控制,双向绑定,表单校验,Redux 的 connect
  • 使用方式:compose 组合多个 HOC、ES7 的 Decorators 模式写法 @xxx
  • 实现方式:
    • 属性代理(Props Proxy),继承 extends Component ,添加逻辑处理,并传递个被包装的组件
    • 反向继承(Inheritance Inversion),继承 extends TargetComponent ,处理原组件的生命周期、props、state、render 等等
  • 注意事项:
    • 静态属性需要手动拷贝,可以使用:hoist-non-react-statics 包
    • 传递 refs,16.3+ 版本,可用使用 forwardRef API 来传递
    • 不要在 render 方法内创建高阶组件,会导致组件每次被卸载后重新挂载
    • 不要改变原始组件,高阶组件就是一个没有副作用的纯函数
    • 透传不相关的 props
    • 设置 displayName ,方便调试,可以用 recompose 包实现
    • 不要修改原始组件,使用组合组件
  • 优点:
    • 高阶组件就是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合
    • 高阶组件也可能造成冲突,但我们可以在遵守约定的情况下避免这些行为
    • 高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处,高阶组件的增加不会为原组件增加负担
  • 缺点:
    • HOC 需要在原组件上进行包裹或者嵌套,大量使用 HOC,将会产生非常多的嵌套,会让调试变的困难
    • HOC 可以劫持 props,在不遵守约定的情况下也可能造成冲突

知识拓展

  • 实现 HOC 的两种模式:

    • Props Proxy: HOC 对传给 WrappedComponent W 的 porps 进行操作,
    • Inheritance Inversion: HOC 继承 WrappedComponent W。
  • Props Proxy

    • 可以做什么:
      • 操作 props
      • 通过 Refs 访问到组件实例
      • 提取 state
      • 用其他元素包裹 WrappedComponent
    • 代码示例:
        function ppHOC(WrappedComponent) {  
          return class PP extends React.Component {    
            render() {      
              return <WrappedComponent {...this.props}/>    
            }  
          } 
        }
    
  • Inheritance Inversion

    • 可以做什么:
      • 渲染劫持(Render Highjacking)
        • 在由 render输出的任何 React 元素中读取、添加、编辑、删除 props
        • 读取和修改由 render 输出的 React 元素树
        • 有条件地渲染元素树
        • 把样式包裹进元素树(就像在 Props Proxy 中的那样)
      • 操作 state
    • 代码示例:
    function iiHOC(WrappedComponent) {
      return class Enhancer extends WrappedComponent {
        render() {
          return super.render()
        }
      }
    }
    
  • 举例:

    • redux ,使用的是 props proxy 模式
    • radium,使用的是 inheritance inversion 模式
      • 过在内联样式中使用CSS 伪类增强了内联样式的能力
      • 使用的是 inheritance inversion 模式
      • github.com/FormidableL…
  • 与 Mixin 对比

    • Mixin 的作用:代码复用
    • Mixin 的注意事项:只有在使用createClass来创建React组件时才可以使用,ES6 语法已废弃
    • Mixin 的缺点:
      • 可能会相互依赖,相互耦合,不利于代码维护
      • 不同的 Mixin 中的方法可能会相互冲突
      • Mixin 非常多时,组件时可以感知的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性
  • 与 Hooks 对比

    • Hooks 的作用:16.8 +版本出现的,可以在 class 以外使用 state 和其他 React 特性,使用 Hooks 可以将在含有 state 的逻辑从组件中抽象出来,这将可以让这些逻辑容易被测试,同时可以在不重写组件结构的情况下复用这些组件。同样也可以作为一种实现状态逻辑复用的方案。
    • Hooks 的注意事项:
      • 只能在 React 函数式组件或者自定义 Hooks 中使用
      • 不要在循环、条件、或嵌套函数中条用 Hook
    • Hooks 的优点:
      • 减少状态逻辑复用的风险,对比 Mixin
      • 避免地狱式嵌套,对比 HOC
      • 让组件更容易理解、使用函数代替 class,对比 class

参考资料

浏览知识共享许可协议

本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。