React 高阶组件

2,193 阅读5分钟

React高阶组件

本文是学习慕课网 React高阶组件课程 的笔记~

一、什么是React高阶组件

高阶组件就是接收一个组件作为参数,然后返回一个新的组件。高阶组件其实是一个函数,并不只是一个组件。

二、入门Demo来认识高阶组件

1、需求分析

加入有某需求:

我们对上图进行组件拆分,可以分为外部modal提示框组件和内部信息两个组件。

外部modal组件是不变的,但是内部的内容,我们可能在不同的地方会显示不同的效果。如下图。

所以我们有必要去封装一个外部的modal提示框组件,去包裹不同的内部组件。

2、项目构建

// 创建一个空项目
create-react-app xxx

我们新建几个文件。

// A/index.js

import React from 'react';
import './index.css';

// 定义一个函数
// 传入一个组件作为参数
function A(WrappedComponent) {
  // 返回一个组件
  return class A extends React.Component {
    constructor (props) {
      super(props);
      this.state = {};
    }

    render () {
      return (
        <div className="a-container">
           <div className="header">
             <div className="title">提示</div>
             <div className="close">X</div>
           </div>
           <div>
             <!-- 在这里使用一下 -->
             <WrappedComponent />
           </div>
         </div>
      )
    }
  }
}

// 抛出函数
export default A

A组件就是我们的外部Modal提示框组件。用来包裹我们的内部组件。

我们来实现B组件。

// B/index.js

import React from 'react';
import A from '../A/index.js';
import './index.css';


class B extends React.Component {
    render() {
        return (
            <div className="wrap">
                <img src="https://raw.githubusercontent.com/freya0608/High-order-component/master/src/imgs/B.png" alt="" />
            </div>
        );
    }
}

<!--调用A()方法去包裹我们的B组件。-->
export default  A(B);

最终实现效果为:

到此一个入门的高阶组件实例就ok了。

总结:使用高阶组件的方式:

第一种就是我们上面的方法,第二种是利用装饰器来实现,具体的配置大家网上可以查到。

现在我们又有了需求,就是B组件的内容,可能决定A组件的标题,所以这时候就需要我们去通过B组件去传值给A了。

// A/index.js

import React from 'react';
import './index.css';

// 这里我们返回一个匿名函数,接收传递得值
export default (title = '我是标题') => {

  // 然后返回一个函数,这个函数接收子组件 
  return (WrappedComponent) => {
    // 返回我们的外部组件
    return class A extends React.Component {
      constructor (props) {
        super(props);
        this.state = {};
      }

      render () {
        return (
          <div className="a-container">
             <div className="header">
               <!--这里使用我们传入的值-->
               <div className="title">{title}</div>
               <div className="close">X</div>
             </div>
             <div>
               <WrappedComponent />
             </div>
           </div>
        )
      }
    }
  }
}
// B/index.js
import React from 'react';
import A from '../A/index.js';
import './index.css';
class B extends React.Component {
    render() {
        return (
            <div className="wrap">
                <img src="https://raw.githubusercontent.com/freya0608/High-order-component/master/src/imgs/B.png" alt="" />
            </div>
        );
    }
}

<!-- 最主要是这里 -->
export default  A('提示i')(B);

这样我们就实现了B给A组件传值,来让A组件能动态的改变某些地方了。

三、代理方式的高阶组件

认识代理方式的高阶组件我们要从 prop、访问ref、抽取状态、包裹组件四个部分来认识。

  • props
// A/index

import React from 'react';
import './index.css';

export default (title = '我是标题') => {
  return (WrappedComponent) => {
    return class A extends React.Component {
      constructor (props) {
        super(props);
        this.state = {};
      }

      render () {
        // [1] 首先我们可以获取到给最外层组件传递的props
        const prop = this.props;
        console.log(prop);

        
        return (
          <div className="a-container">
             <div className="header">
               <div className="title">{title}</div>
               <div className="close">X</div>
             </div>
             <div>
               <!--[2] 可以传递下去,传到B组件中-->
               <!--[3] 另外sex是新增的-->
               <WrappedComponent sex={'男'} {...this.props} />
             </div>
           </div>
        )
      }
    }
  }
}

// B/index.js

import React from 'react';
import A from '../A/index.js';
import './index.css';
class B extends React.Component {
    render() {
        return (
            <div className="wrap">
                <!--[1]在B组件中就可以拿到通过A组件传来的props-->
                我的姓名: {this.props.name}
                我的性别: {this.props.sex}
                <img src="https://raw.githubusercontent.com/freya0608/High-order-component/master/src/imgs/B.png" alt="" />
            </div>
        );
    }
}


export default  A('提示i')(B);

在APP中我们可以设置props,传递到A组件,再由A组件筛选或者增加props,之后传给B,这样在B就能接收到最外层以及A传递来的props。同样由于A是中间层,A有权限控制B能得到哪些props,也能额外增加一些属性过去。

<B name={'zjj'}></B>
  • refs
// A/index

import React from 'react';
import './index.css';

export default (title = '我是标题') => {
  return (WrappedComponent) => {
    return class A extends React.Component {
      constructor (props) {
        super(props);
        this.state = {};
      }
      // [1] 定义一个点击事件
      handleClick = () => {
        this.wref.getName();
      }


      render () {
        const prop = this.props;
        console.log(prop);
        return (
          <div className="a-container">
             <div className="header">
               <div className="title">{title}</div>
               <div className="close">X</div>
             </div>
             <div>
                <!-- 【2】绑定ref -->
               <WrappedComponent ref={ (v) => this.wref = v } sex={'男'} {...this.props} />
             </div>
             <div>
             <!--【3】点击事件触发处-->
              <button onClick={this.handleClick}>获取name</button>
             </div>
           </div>
        )
      }
    }
  }
}

我们在B中定义一个getName方法。那么通过点击A中的按钮,就可以调用到

B/index

getName = () => {
  console.log('获取到了name')
}
  • 抽取状态
// A.js
import React from 'react';
import './index.css';

export default (title = '我是标题') => {
  return (WrappedComponent) => {
    return class A extends React.Component {
      constructor (props) {
        super(props);
        this.state = {
          value: ''
        };
      }

      // 点击
      handleClick = () => {
        this.wref.getName();
      }

      // [1] 根据输入设置 val
      handleOnInputChange = (e) => {
        this.setState({
          value: e.target.value
        })
      }

      render () {
        const prop = this.props;
        console.log(prop);
        
        // [2] 设置新的props
        var newProps = {
          value: this.state.value, // 传入值
          onInput: this.handleOnInputChange // 监听表单的输入
        }
        return (
          <div className="a-container">
             <div className="header">
               <div className="title">{title}</div>
               <div className="close">X</div>
             </div>
             <div>
               <WrappedComponent ref={ (v) => this.wref = v } sex={'男'} {...this.props}  {...newProps} />
               // 【3】传入子组件
             </div>
             <div>
              <button onClick={this.handleClick}>获取name</button>
             </div>
           </div>
        )
      }
    }
  }
}

// B/index.js
// 使用传入的值,这样便可以将子组件内部的实现抽取出来,放到公共的组件中去,统一管理
<input type="text" value={this.props.value} onInput={this.props.onInput} />
  • 包装组件

其实我们上面就是实现了包裹内部组件的效果。