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);
}
相关推荐
郑大乾66618 分钟前
vuex - 第一天
javascript·vue.js·node.js
阿卡基YUAN20 分钟前
JavaScript 箭头函数
前端·javascript
湛海不过深蓝26 分钟前
【js】记录预览pdf文件
开发语言·javascript·pdf
兮动人32 分钟前
vue之axios基本使用
前端·javascript·vue.js
嵌入式小强工作室1 小时前
STM32 Flash DB的使用方法
前端·javascript·stm32
qlj2242 小时前
react-native键盘遮盖底部输入框问题修复
javascript·react native·react.js
GISer_Jing2 小时前
Javascript数据结构常见面试题目(全)
javascript·数据结构·面试
小溪彼岸3 小时前
【React】React Router
前端·react.js
十里八乡有名的后俊生3 小时前
深入探索前端开发中的浏览器事件模型与请求处理
前端·javascript·浏览器
坚定信念,勇往无前3 小时前
vue,javascript 可选链
前端·javascript·vue.js