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} />
複製程式碼
- 包裝元件
其實我們上面就是實現了包裹內部元件的效果。