React中的高阶组件

高阶组件

HOC 是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 组合特性而形成的设计模式

参数为组件,返回值为新函数

js 复制代码
const EnhanceComponent = higherOrderComponent(WrappedComponent);

组件将 props 转换为 UI,高阶组件将组件转换为另一个组件

如: Redux 的 connect

属性代理
js 复制代码
function HOC(WrapCompnent) {
  return class Advance extends React.Component {
    state = {
      name: "benben",
    };

    render() {
      return <WrapCompnent {...this.props} {...this.state} />;
    }
  };
}

优点:

  1. 属性代理可以和业务组件低耦合,零耦合,对于条件渲染和 props 属性增强,只负责控制子组件渲染和传递额外的 props
    所以无需知道业务组件做了什么
  2. 同样适用于类组件和函数组件
  3. 可以完全隔离业务组件的渲染,因为属性代理可以理解为一个新组件,可以控制业务组件是否渲染
  4. 可以嵌套使用,多个 HOC 可以嵌套使用,而且一般不会限制包装 HOC 的先后顺序

缺点:

  1. 一般无法直接获取原始组件的状态,如果想要获取,需要通过 ref 获取组件实例
  2. 无法直接集成静态属性。如果需要继承则要手动处理,或者引入第三方库
  3. 本质上是产生了一个新组件,所以需要配合 forwardRef 来转发 ref
反向继承
  • 包装后的组件继承了原始组件本身,所以此时无须再去挂载业务组件
js 复制代码
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 嵌套在一起,当前状态会覆盖上一个状态,比如说 componentDidMount 这样的副作用串联起来

渲染劫持

js 复制代码
class HOC = (WrapComponent) => {
  class Index extends WrapComponent {
    render() {
      if(this.props.visible) {
        return super.render()
      } else {
        return <div>no content</div>
      }
    }
  }
}
js 复制代码
class Index extends React.Component {
  render() {
    return (
      <div>
        <ul>
          <li>react</li>
          <li>vue</li>
          <li>Angular</li>
        </ul>
      </div>
    );
  }
}
function HOC(Component) {
  return class Advance extends Component {
    render() {
      const element = super.render();
      const otherProps = {
        name: "alien",
      };
      /* 替换 Angular 元素节点 */
      const appendElement = React.createElement(
        "li",
        {},
        `hello ,world , my name  is ${otherProps.name}`
      );
      const newchild = React.Children.map(
        element.props.children.props.children,
        (child, index) => {
          if (index === 2) return appendElement;
          return child;
        }
      );
      return React.cloneElement(element, element.props, newchild);
    }
  };
}
export default HOC(Index);

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

  1. 强化 props ,可以通过 HOC ,向原始组件混入一些状态。
  2. 渲染劫持,可以利用 HOC ,动态挂载原始组件,还可以先获取原始组件的渲染树,进行可控性修改。
  3. 可以配合 import 等 api ,实现动态加载组件,实现代码分割,加入 loading 效果。
  4. 可以通过 ref 来获取原始组件实例,操作实例下的属性和方法。
  5. 可以对原始组件做一些事件监听,错误监控等。
手写HOC
js 复制代码
function withRouter(Component) {
  const displayName = `withRouter (${Component.displayName || Component.name})`;
  const C = (props) => {
    const { wrapperComponentRef, ...remainingProps } = props;
    return (
      <RouterContext.Consumer>
        {(context) => {
          return (
            <Component
              {...remainingProps}
              {...context}
              ref={wrapperComponentRef}
            />
          );
        }}
      </RouterContext.Consumer>
    );
  };

  C.displayName = displayName;
  C.wrapperComponent = Component;
  return hoistStatics(C, Component);
}
相关推荐
西哥写代码13 分钟前
基于cornerstone3D的dicom影像浏览器 第十八章 自定义序列自动播放条
前端·javascript·vue
清风细雨_林木木17 分钟前
Vue 中生成源码映射文件,配置 map
前端·javascript·vue.js
雪芽蓝域zzs1 小时前
JavaScript splice() 方法
开发语言·javascript·ecmascript
霸王蟹1 小时前
React中巧妙使用异步组件Suspense优化页面性能。
前端·笔记·学习·react.js·前端框架
Coding的叶子1 小时前
React Flow 节点属性详解:类型、样式与自定义技巧
react.js·node·节点·fgai·react agent
森叶2 小时前
Electron 主进程中使用Worker来创建不同间隔的定时器实现过程
前端·javascript·electron
霸王蟹2 小时前
React 19 中的useRef得到了进一步加强。
前端·javascript·笔记·学习·react.js·ts
霸王蟹2 小时前
React 19版本refs也支持清理函数了。
前端·javascript·笔记·react.js·前端框架·ts
codelxy2 小时前
vue引用cesium,解决“Not allowed to load local resource”报错
javascript·vue.js
程序猿阿伟3 小时前
《社交应用动态表情:RN与Flutter实战解码》
javascript·flutter·react native