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);
}
相关推荐
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄1 天前
JavaScript 隐式全局变量解析
javascript
沙蒿同学1 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
paopaokaka_luck2 天前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
Hilaku2 天前
为什么死磕 1px 的团队,用户体验反而更差?
前端·javascript·程序员
陈拉斯2 天前
给公司年会写了个大屏抽奖系统:动画在前端跑,凭什么说结果没被改?
javascript
yzy852 天前
Vue中下载或打开文件的JavaScript方法
前端·javascript·vue
用户298698530142 天前
在 React 中用 JavaScript 将 Word 转换为文本
前端·javascript·react.js
李少兄2 天前
深入解析 JavaScript 中的 `document` 对象
javascript