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);
}
相关推荐
PleaSure乐事10 分钟前
【Node.js】内置模块FileSystem的保姆级入门讲解
javascript·node.js·es6·filesystem
雷特IT20 分钟前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript
awonw1 小时前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
老齐谈电商1 小时前
Electron桌面应用打包现有的vue项目
javascript·vue.js·electron
柏箱2 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
一颗花生米。5 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐015 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19955 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&6 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
一路向前的月光11 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js