React HOC的两种实践

HOC简介

HOC全称为High Order Component即高阶组件, 其使用姿势通常有两种:

属性代理(Props Proxy)

  • 操作props
  • 抽象state
  • 获取ref
  • 用其他组件包裹(功能集成/样式包裹)

简而言之: 修改传入组件的props.
使用姿势常常是这样的:

const ppHoc = WrappedComponent => class extends React.Component {
    render() {
        // 此处可以修改props,或者注入state,
        // 总之对WrappedComponent而言就是修改了props
        return  <WrappedComponent {...this.props} />
    }
}

反向继承(Inheritance Inversion)

  • 渲染劫持 render hijacking
  • 操纵state

使用姿势常常是这样的:

const iiHoc = WrappedComponent => class extends WrappedComponent{
    render() {
        const elementTree = super.render();
        const { props } = elementTree;
        // 可以修改props
        const newProps = {aa: 1};

        return React.cloneElement(elementTree, {...props, ...newProps}, elementsTree.props.children)
    }
}

继承该组件,可基于其elementTree进行修改。能力更强,但风险也更高。

不能保证完整的子组件树被解析, 以及静态方法不会被继承。

实践

需求简介:

目前页面中已有多个图表组件(单一维度的数据统计折线图),目前想为每个图表添加checkBox,可以交叉其他维度进行统计。

图1,2,3

需求分析:

  • 目前业务中每个图表是一个封装好的组件,(如图一所示,标题一行和图表是一体的,为包装好的Chart组件)。现在业务中要为每个图表都加一个CheckBox。即,需要将每个图表组件进行再次包装,将check state与chart组合成一个Component.

  • 如果checkbox位置如图2所示,则checkBox可以作为图表组件的children,也可以作为兄弟组件,只要调整下其位置即可。

  • 倘若checkBox要如图3,放在title和图表中间,则需要将CheckBox作为Chart的children才能插入到该位置,否则是没有空间放checkbox。如何才能以较低成本,给十来个Chart组件都添加CheckBox这个Children呢?此时就只能通过Hoc修改其props.children来实现了

按照上图所示布局,我们通过两种HOC方式都来实践下:

实践1: 属性代理

组件结构如下,state保存在Parent中,CheckBox和Chart是兄弟组件。当isChecked切换状态时,修改Chart对应的props.

Parent
    CheckBox
    Chart

主要代码如下

const interHoc = WrappedComponnet =>
  class extends React.Component {
    state = {
      isChecked: false,
    };

    render() {
      const { isChecked } = this.state;
      let chartProps = { ...this.props };

      //  修改props
      const {
        formatParams: { dims = [] },
      } = chartProps;
      const GENDER_TYPE = 'predicted_gender';

      if (isChecked && !dims.includes(GENDER_TYPE)) {
        chartProps.formatParams.dims = [GENDER_TYPE].concat(dims);
      } else {
        chartProps.formatParams.dims = dims.filter(d => d !== GENDER_TYPE);
      }

      return (
        <div>
          <CheckBox
            checked={isChecked}
            onChange={e => this.setState({ isChecked: e.target.value })}
          >
            交叉性别维度
          </CheckBox>
          <WrappedComponnet {...chartProps} />
        </div>
      );
    }
  };

此处是通过包裹另外组件实现的,也可以直接修改props.chilren = YourComponent实现。

实践2:渲染劫持

通过继承WrappedComponent,获取其elementTree, 根据原props中的参数,符合条件的,对其props和props.children进行修改。

通过继承可以直接修改elementTree(修改其props和children)显然能力范围是更强大的,但风险也更高,能不用就不用吧。

const interHoc = WrappedComponent =>
  class extends WrappedComponent {
    state = {
      isChecked: false,
    };

    render() {
      const { isChecked } = this.state;

      const elementTree = super.render();

      const interCom = (
        <Checkbox
          checked={isChecked}
          onChange={e => this.setState({ isChecked: e.target.checked })}
        >
          交叉性别维度
        </Checkbox>
      );

      // 修改props
      const {
        props: {
          formatParams: { dims = [] },
        },
      } = elementTree;

      const GENDER_TYPE = 'predicted_gender';

      elementTree.props.children = interCom;
      if (isChecked && !dims.includes(GENDER_TYPE)) {
        elementTree.props.formatParams.dims = [GENDER_TYPE].concat(dims);
      } else {
        elementTree.props.formatParams.dims = dims.filter(
          i => i !== GENDER_TYPE,
        );
      }

      return elementTree;
    }
  };
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • React进阶之高阶组件 前言 本文代码浅显易懂,思想深入实用。此属于react进阶用法,如果你还不了解react...
    流动码文阅读 1,191评论 0 1
  • 高阶组件是react应用中很重要的一部分,最大的特点就是重用组件逻辑。它并不是由React API定义出来的功能,...
    叫我苏轼好吗阅读 914评论 0 0
  • 函数式编程,对应的是声明式编程,声明式编程的本质的lambda验算(是一个匿名函数,即没有函数名的函数。Lambd...
    不安分的三好份子阅读 1,041评论 0 1
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,878评论 0 24
  • 原文地址:React is Slow, React is Fast: Optimizing React Apps ...
    吖吓阅读 9,931评论 1 13