你不知道的React系列(三十三)高阶组件

109 阅读2分钟

本文正在参加「金石计划」

概念

  • 复用组件逻辑,基于 React 的组合特性而形成的设计模式

  • 高阶组件是参数为组件,返回值为新组件的函数

    const EnhancedComponent = higherOrderComponent(WrappedComponent);
    
  • Redux 的 connect 和 Relay 的 createFragmentContainer

  • 无法使用组件复用逻辑时使用高阶组件

  • HOC 不会修改传入的组件,也不会使用继承来复制其行为

  • HOC 是纯函数,没有副作用

  • 被包装组件接收来自容器组件的所有 prop

  • HOC 不需要关心数据的使用方式或原因,而被包装组件也不需要关心数据是怎么来的

  • 可以根据需要对参数进行修改

  • 不要在HOC里面使用包裹组件的相关逻辑,而应该使用组合

约定

  • HOC 应该透传与自身无关的 props

  • 最大化可组合性

    • 定义另一个返回高阶组件的高阶函数

      // React Redux 的 `connect` 函数
      const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
      
      // connect 是一个函数,它的返回值为另外一个函数。
      const enhance = connect(commentListSelector, commentListActions);
      // 返回值为 HOC,它会返回已经连接 Redux store 的组件
      const ConnectedComment = enhance(CommentList);
      
    • 输出类型与输入类型相同的函数很容易组合在一起

      // 不推荐如下写法...
      const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))
      
      // ... 建议编写组合工具函数
      // compose(f, g, h) 等同于 (...args) => f(g(h(...args)))
      const enhance = compose(
        // 这些都是单参数的 HOC
        withRouter,
        connect(commentSelector)
      )
      const EnhancedComponent = enhance(WrappedComponent)
      
  • HOC命名规则,组件名(包裹组件名称)

    function withSubscription(WrappedComponent) {
      class WithSubscription extends React.Component {/* ... */}
      WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
      return WithSubscription;
    }
    
    function getDisplayName(WrappedComponent) {
      return WrappedComponent.displayName || WrappedComponent.name || 'Component';
    }
    

注意事项

  • 复制静态方法

    • 把静态方法拷贝到容器组件

      function enhance(WrappedComponent) {
        class Enhance extends React.Component {
          /*…*/
        }
        // 必须准确知道应该拷贝哪些方法
        Enhance.staticMethod = WrappedComponent.staticMethod;
        return Enhance;
      }
      
    • github.com/mridgway/ho…

      import hoistNonReactStatic from 'hoist-non-react-statics';
      function enhance(WrappedComponent) {
        class Enhance extends React.Component {/*...*/}
        hoistNonReactStatic(Enhance, WrappedComponent);
        return Enhance;
      }
      
    • 额外导出静态方法

      // 使用这种方式代替...
      MyComponent.someFunction = someFunction;
      export default MyComponent;
      
      // ...单独导出该方法...
      export { someFunction };
      
      // ...并在要使用的组件中,import 它们
      import MyComponent, { someFunction } from './MyComponent.js';
      
  • Refs 不会被传递

    ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件