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} />
- 包装组件
其实我们上面就是实现了包裹内部组件的效果。