前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React 中高阶函数与高阶组件(下)

React 中高阶函数与高阶组件(下)

作者头像
itclanCoder
发布2020-12-01 10:27:56
7470
发布2020-12-01 10:27:56
举报
文章被收录于专栏:itclanCoderitclanCoder

虽互不曾谋面,但希望能和您成为笔尖下的朋友

以读书,技术,生活为主,偶尔撒点鸡汤

不作,不敷衍,意在真诚吐露,用心分享

点击左上方,可关注本刊

标星公众号(ID:itclanCoder)

如果不知道如何操作

点击这里,标星不迷路

前言

上一节React 中高阶函数与高阶组件(上)介绍了React中的高阶函数以及高阶组件,高阶函数具体有哪些应用以及什么是高阶组件,如何编写高阶组件

那么React中高阶组件又有哪些应用呢

01

React 中高阶组件的应用

代理方式的高阶组件

返回的新组件直接继承自React.Component类,新组件扮演的角色传入参数组件的一个代理,在新组件的 render 函数中,将被包裹组件渲染出来,除了高阶组件自己要做的工作,其余功能全都转手给了被包裹的组件

应用场景

应用 1-操做 props

高阶组件能够改变被包裹组件的props,可以对props进行任何操做,甚至可以在高阶组件上自定义事件,然后通过 props 传递下去 如下 ComponentB 组件

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA';

@A
class ComponentB extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <div className="component-b">
          <div className="list">我的名字是: {this.props.name}</div>
          <div className="list">我的网站是: {this.props.site}</div>
          <div className="list">C</div>
          <div className="list">D</div>
        </div>
      </div>
    );
  }
}

export default ComponentB;

如下是调用组件App.js

代码语言:javascript
复制
import React, { Component } from 'react';

import ComponentB from './components/popcomponent/componentB';
import ComponentC from './components/popcomponent/componentC';
import ComponentF from './components/popcomponent/componentF';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className="App">
        <ComponentB name={'川川'} site={'itclanCoder'} />
        <ComponentC />
        <ComponentF />
      </div>
    );
  }
}

export default App;

这个时候,你会发现在componentB组件中的props中拿不到App.js中传递过来的 name 和 site 属性,原因是,我们属性传递到高阶组件componentA里面,但是我们componentA组件没有把属性传给被包裹组件,这就导致被包裹的componentB组件拿不到namesite属性

此时,需要在高阶组件componentA中进行改写,将传递到高阶组件属性解构出来并传递给被包裹的属性

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentA.css';

function A(WrappendComponent) {
  return class ComponentA extends Component {
    constructor(props) {
      super(props);
    }

    render() {
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              // 要将传递到高阶组件的属性解构出来并传递给被包裹的组件
              <WrappendComponent {...this.props} />
            </div>
          </div>
        </div>
      );
    }
  };
}

export default A;

这样就可以达到给组件传递属性,渲染结果如下所示

同样可以给高阶组件新增或者修改属性

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentA.css';

function A(WrappendComponent) {
  // 高阶组件A
  return class ComponentA extends Component {
    constructor(props) {
      super(props);
    }

    render() {
      // 如何通过高阶组件删除被包裹组件的属性,将属性解构出来
      const { site, ...getattrs } = this.props; // 获取属性
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              // 也可以通过高阶组件给被包裹组件增加属性,例如这里的job
              <WrappendComponent job={'搬砖'} {...getattrs} /> // 也可以设置属性
            </div>
          </div>
        </div>
      );
    }
  };
}

export default A;

渲染出来的结果如下所示

应用 2-访问 ref

如下是高阶组件componentA.js

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentA.css';

function A(WrappendComponent) {
  return class ComponentA extends Component {
    constructor(props) {
      super(props);
    }

    constrolRef(instance) {
      instance.getName && instance.getName();
    }

    render() {
      // const { site, ...getattrs } = this.props;
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              <WrappendComponent
                ref={this.constrolRef.bind(this)}
                job={'搬砖'}
                {...this.props}
              />
            </div>
          </div>
        </div>
      );
    }
  };
}

export default A;

如下是componentB组件

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA';

@A
class ComponentB extends Component {
  constructor(props) {
    super(props);
  }

  getName() {
    console.log('访问到了的');
  }

  render() {
    return (
      <div>
        <div className="component-b">
          <div className="list">我的名字是: {this.props.name}</div>
          <div className="list">我的网站是: {this.props.site}</div>
          <div className="list">我的职业是: {this.props.job}</div>
          <div className="list">D</div>
        </div>
      </div>
    );
  }
}

export default ComponentB;

通过以上操作在 componentB 中就能够拿到 ref 的属性了的

应用 3-抽取状态

假如我们的高阶组件包裹的都有同工的一个方法,例如:一个输入框,希望让这个输入框受控此时就要监听这个输入框的input

每次输入值就使用一次setState让输入框内容也跟着改变,如果在各个组件中都自己实现了这个方法,那么就会造成很多重复的工作,此时可以利用高阶组件帮我们去抽离状态 commponentA.js

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentA.css';

function A(WrappendComponent) {
  return class ComponentA extends Component {
    constructor(props) {
      super(props);

      // 把状态统一抽离到高阶组件里
      this.state = {
        value: '',
      };
    }

    // 把方法统一抽离到高阶组件里面
    handleInputChange = (e) => {
      this.setState({
        value: e.target.value,
      });
    };

    constrolRef(instance) {
      instance.getName && instance.getName();
    }

    render() {
      const newProps = {
        value: this.state.value,
        onChange: this.handleInputChange,
      };
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              {/* 把状态和方法传给被包裹组件 */}
              <WrappendComponent {...newProps} />
            </div>
          </div>
        </div>
      );
    }
  };
}

export default A;

componentB.js

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentB.css';
import A from './componentA';

@A
class ComponentB extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <div className="component-b">
          <div className="list">
            <input {...this.props} />
          </div>
          <div className="list">我的名字是: {this.props.name}</div>
          <div className="list">我的网站是: {this.props.site}</div>
          <div className="list">我的职业是: {this.props.job}</div>
          <div className="list">D</div>
        </div>
      </div>
    );
  }
}

export default ComponentB;

这是组件 componentC.js

代码语言:javascript
复制
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './componentC.css';
import A from './componentA';

class ComponentC extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <div>
          <input {...this.props} />
        </div>
        <div className="component-c">
          <div className="component-list">A</div>
          <div className="component-list">B</div>
          <div className="component-list">C</div>
          <div className="component-list">D</div>
        </div>
      </div>
    );
  }
}

export default A(ComponentC);

如下是实例效果

这样,我们就在高阶组件中把公共的状态给抽离出来了的,提高代码的复用性,相当于是把各个组件的状态放到公共组件管理了的

然后通过 props 的方式传给了各个组件

包装组件

所谓包装组件就是添加一些列的标签,让被包裹组件实现想要的样式

代码语言:javascript
复制
import React, { Component } from 'react';

function A(WrappendComponent) {
  return class ComponentA extends Component {
    render() {
      const newProps = {
        value: this.state.value,
        onChange: this.handleInputChange,
      };
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              {/* 把状态和方法传给被包裹组件 */}
              <WrappendComponent {...newProps} />
            </div>
          </div>
        </div>
      );
    }
  };
}

export default A;

总得来说代理方式的高阶组件形式如下所示

代码语言:javascript
复制
export default () => (wrappedComponent) =>
  class A extends Component {
    render() {
      const { ...otherProps } = this.props;
      return <WrappendComponent {...otherProps} />;
    }
  };

02

继承方式的高阶组件

采用继承关联作为参数的组件和返回的组件,加入传入的参数wrappedComponent,那么返回的组件直接继承自wrappedComponent

代码语言:javascript
复制
export default () => (wrappedComponent) =>
  class A extends wrappedComponent {
    render() {
      const { user, ...otherProps } = this.props;
      this.props = otherProps;
      return super.render();
    }
  };

继承方式的高阶组件,继承方式是参数 wrappedComponent,而代理的高阶组件直接是 component,返回的结果也不同,代理高阶组件的返回值是参数的返回值,而继承方式是直接返回一个super.render

⒈ 操作 props

如下是componentH继承方式组件,定义了两个组价componentIcomponentJ

继承组件 componentH

代码语言:javascript
复制
import React from 'react';

function componentH(wrappedComponent) {
  return class NewComponent extends wrappedComponent {
    render() {
      const element = super.render();
      const newStyle = {
        color: element.type == 'div' ? 'red' : 'green',
      };
      const newProps = { ...this.props, style: newStyle };
      return React.cloneElement(element, newProps, element.props.children);
    }
  };
}

export default componentH;

componentI 组件

代码语言:javascript
复制
import React, { Component } from 'react';
import componentH from './componentH';

@componentH
class componentI extends Component {
  render() {
    return <div>我是div元素</div>;
  }
}

export default componentI;

componentJ 组件

代码语言:javascript
复制
import React, { Component } from 'react';
import componentH from './componentH';

@componentH
class componentJ extends Component {
  render() {
    return <span>我是span元素</span>;
  }
}

export default componentJ;

使用组件

代码语言:javascript
复制
import React, { Component } from 'react';

import ComponentI from './components/popcomponent/componentI';
import ComponentJ from './components/popcomponent/componentJ';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div className="App">
        <ComponentI />
        <ComponentJ />
      </div>
    );
  }
}

export default App;

输出结果

说明

通过以上例子发现,使用继承方式的高阶组件相比代理方式高阶组件,是一件非常麻烦的操作,除非需要通过传入的参数组件来判断性的去修改一些属性,否则就没有必要使用继承方式高阶组件去操作props

⒉ 操纵生命周期函数

继承方式的高阶组件需要修改生命周期函数直接在高阶组件内重写生命周期函数就可以了的,它会覆盖掉参数组件的生命周期函数

结论

使用代理方式的高阶组件要优于继承方式的高阶组件,所以应优先使用代理方式的高阶组件

03

如何显示高阶组件名

显示高阶组件名,是为了更好的 debug 调试,如果没有进行设置,只能通过查看源代码的方式

无法在浏览器中非常的直观看到

未设置高阶组件名

设置组件名称

代码语言:javascript
复制
static displayName = `高阶组件名字(${getDisplayName(参数)})`

function getDisplayName(参数) {
  return 参数.displayName || 参数.name || 'Component默认名字'
}

完整示例

代码语言:javascript
复制
import React, { Component } from 'react';
import './componentA.css';

function A(WrappendComponent) {
  return class ComponentA extends Component {
    constructor(props) {}

    static displayName = `A(${getDisplayName(WrappendComponent)})`;

    render() {
      return (
        <div>
          <div className="pop-box">
            <div className="header">
              <div>提示</div>
              <div>X</div>
            </div>
            <div className="content">
              <WrappendComponent
                ref={this.constrolRef.bind(this)}
                job={'搬砖'}
                {...this.props}
              />
            </div>
          </div>
        </div>
      );
    }
  };
}

function getDisplayName(WrappendComponent) {
  return WrappendComponent.displayName || WrappendComponent.name || 'Component';
}

export default A;

示例结果

结语

本节主要讲述了 React 中的高阶函数以及高阶组件的使用,所谓高阶函数就是一个函数可以被当做参数传递,返回值也可以是函数作为输出

而高阶组件,是以接收一个组件作为参数并返回一个新的组件(类)的函数,并有代理式高阶组件,继承式高阶组件

以及装饰器的使用,显示高阶组件名称等

如果您有对 React 中高阶组件以及高阶函数有疑问,欢迎下方留言,一起讨论

原文出处:https://coder.itclan.cn/fontend/framework/advance-highfun-and-component/


公众号(ID:itclanCoder)

码能让您早脱菜籍,文能让您洗净铅华

可能您还想看更多:

文章都看完了

不点个在看吗

戳原文,阅读体验会更好哦

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 itclanCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 01
    • 代理方式的高阶组件
    • 02
    • 03
      • 结语
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档