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);
}
相关推荐
Ciito7 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
fighting ~43 分钟前
react17安装html-react-parser运行报错记录
javascript·react.js·html
老码沉思录1 小时前
React Native 全栈开发实战班 - 列表与滚动视图
javascript·react native·react.js
abments1 小时前
JavaScript逆向爬虫教程-------基础篇之常用的编码与加密介绍(python和js实现)
javascript·爬虫·python
老码沉思录1 小时前
React Native 全栈开发实战班 - 状态管理入门(Context API)
javascript·react native·react.js
文军的烹饪实验室2 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
老码沉思录4 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
老码沉思录4 小时前
React Native 全栈开发实战班 - 第四部分:用户界面进阶之动画效果实现
react native·react.js·ui
我不当帕鲁谁当帕鲁4 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂4 小时前
工程化实战内功修炼测试题
前端·javascript