上一篇文章介紹了簡單的高階組件實(shí)現(xiàn)方式父丰,接下來介紹代理和繼承方式的高階組件。
一掘宪、代理方式的高階組件
應(yīng)用場景:
1. 操縱props
高階組件能夠改變被包裹組件的 props 蛾扇,可以對 props 做任何操作,甚至可以在高階組件中自定義事件魏滚,然后通過 props 傳遞下去镀首。
上一篇實(shí)現(xiàn)了一個(gè)簡單的高階組件,下面介紹如何在高階組件中傳遞被包裹組件的屬性:
App.js
import React from 'react';
import B from './components/B';
import C from './components/C';
import './index.css';
class App extends React.Component {
render() {
return(
<div className="out-box">
{/* 這里傳遞name和age屬性給B組件 */}
<B name={"Tom"} age={22} />
<C />
</div>
)
}
}
export default App;
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
render() {
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
<Wrapper />
</div>
</div>
)
}
}
};
export default hocA;
B.js
import React, { Component } from 'react';
// 引入該高階組件
import hocA from './A';
class B extends Component {
render() {
return (
<div>
<div>
姓名:{this.props.name}
</div>
<div>
年齡:{this.props.age}
</div>
<div>
這是B組件
</div>
</div>
)
}
}
export default hocA(B);
-
此時(shí) B 組件的效果
image.png
可以看到 B 組件的 props 中拿不到我們傳遞的 name 和 age 屬性鼠次。原因在于我們屬性傳遞到高階組件 A 里面更哄,但是我們 A 組件沒有把屬性傳給被包裹組件,這就導(dǎo)致了被包裹的 B 組件拿不到 name 和 age 屬性须眷。
- 改寫后的
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
render() {
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
{/* 將傳遞到高階組件的屬性解構(gòu)出來并傳遞給被包裹屬性 */}
<Wrapper {...this.props} />
</div>
</div>
)
}
}
};
export default hocA;
- 頁面效果
image.png
如何通過高階組件給被包裹組件增加屬性 A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
render() {
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
{/* 增加sex屬性 */}
<Wrapper {...this.props} sex={"男"} />
</div>
</div>
)
}
}
};
export default hocA;
- B 組件中使用該屬性竖瘾,
B.js
import React, { Component } from 'react';
// 引入該高階組件
import hocA from './A';
class B extends Component {
render() {
return (
<div>
<div>
姓名:{this.props.name}
</div>
<div>
年齡:{this.props.age}
</div>
<div>
性別:{this.props.sex}
</div>
<div>
這是B組件
</div>
</div>
)
}
}
export default hocA(B);
- 頁面效果
image.png
如何通過高階組件刪除被包裹組件的屬性 - 改寫后的
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
render() {
{/* 將props都解構(gòu)出來沟突,除了age屬性以外的其它屬性都用newProps來存放 */}
const { age, ...newProps } = this.props;
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
<Wrapper {...newProps} sex={"男"} />
</div>
</div>
)
}
}
};
export default hocA;
-
頁面效果
image.png
2. 訪問被包裹組件的ref
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
// instance就是傳入的被包裹組件Wrapper
controlRef(instance) {
instance.getName && instance.getName();
}
render() {
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
<Wrapper ref={this.controlRef.bind(this)} />
</div>
</div>
)
}
}
};
export default hocA;
B.js
import React, { Component } from 'react';
import hocA from './A';
class B extends Component {
getName() {
console.log('訪問到了');
}
render() {
return (
<div>
<div>
這是B組件
</div>
</div>
)
}
}
export default hocA(B);
此時(shí)我們?nèi)ロ撁婵刂婆_就可以看到執(zhí)行了被包裹組件 B 里面的 getName 方法了花颗。
3. 抽離狀態(tài)
舉個(gè)例子:
假如我們高階組件包裹的組件都有共同的一個(gè)方法,比如說一個(gè)輸入框惠拭,我們希望讓這個(gè)輸入框受控扩劝,那么我們就要監(jiān)聽這個(gè)輸入框的 input 事件了,每次輸入值就使用 setState 讓輸入框內(nèi)容也跟著改變职辅。如果我們在各個(gè)組件中都自己實(shí)現(xiàn)這個(gè)方法棒呛,那么就會造成很多重復(fù)的工作。此時(shí)可以利用高階組件幫我們?nèi)コ殡x狀態(tài)域携。
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
constructor(props) {
super(props);
// 把狀態(tài)統(tǒng)一抽離到高階組件里
this.state = {
value: ''
}
}
// 把方法統(tǒng)一抽離到高階組件里
handleChange = (e) => {
this.setState({
value: e.target.value
})
}
render() {
const newProps = {
value: this.state.value,
onChange: this.handleChange
}
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
{ /* 把狀態(tài)和方法傳給被包裹組件 */ }
<Wrapper {...newProps} />
</div>
</div>
)
}
}
};
export default hocA;
B.js
import React, { Component } from 'react';
import hocA from './A';
class B extends Component {
render() {
return (
<div>
{ /* 讓高階組件幫我們實(shí)現(xiàn)輸入框受控 */ }
<input {...this.props} />
<div>
這是B組件
</div>
</div>
)
}
}
export default hocA(B);
C.js
import React, { Component } from 'react';
import hocA from './A';
// 裝飾器語法使用該高階組件
@hocA
class C extends Component {
render() {
return (
<div>
{ /* 讓高階組件幫我們實(shí)現(xiàn)輸入框受控 */ }
<input {...this.props} />
<div>
這是C組件
</div>
</div>
)
}
}
export default C;
-
頁面效果
image.png
這樣我們就在高階組件中把公用狀態(tài)抽離出來簇秒,提高了代碼的復(fù)用性。
4. 包裝組件
包裝組件很簡單秀鞭,我們前面就使用了該特性趋观,所謂包裝組件就是添加一系列標(biāo)簽,讓被包裹組件實(shí)現(xiàn)想要的樣式锋边。
二皱坛、繼承方式的高階組件
采用繼承關(guān)聯(lián)作為參數(shù)的組件和返回的組件,假如傳入的組件參數(shù)是Wrapper豆巨,那么返回的組件就直接繼承自 Wrapper 剩辟。
和代理方式的高階組件的區(qū)別
- 兩者繼承的類不同。代理方式的高階組件繼承自 React.Component,繼承方式的高階組件則是繼承自傳入的參數(shù)組件 贩猎。
- render 函數(shù)中 return 出去的東西不同熊户。代理方式的高階組件返回的是傳入的參數(shù)組件,繼承則是返回 super.render()吭服,渲染出參數(shù)組件敏弃。
應(yīng)用場景:
1. 操縱props
使用繼承方式的高階組件給參數(shù)組件添加屬性
A.js
import React from 'react';
function hocA(Wrapper) {
return class A extends Wrapper {
render() {
// 拿到參數(shù)組件的元素
const element = super.render();
const newStyle = {
// 如果參數(shù)組件元素的最外層是div標(biāo)簽則返回紅色,否則返回綠色
color: element.type === 'div' ? 'red' : 'green',
};
const newProps = { ...this.props, style: newStyle };
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
{React.cloneElement(element, newProps, element.props.children)}
</div>
</div>
)
}
}
};
export default hocA;
B.js
import React, { Component } from 'react';
import hocA from './A';
class B extends Component {
render() {
return (
<div>
這是B組件
</div>
)
}
}
export default hocA(B);
C.js
import React, { Component } from 'react';
import hocA from './A';
// 裝飾器語法使用該高階組件
@hocA
class C extends Component {
render() {
return (
<span>
這是組件C
</span>
)
}
}
export default C;
-
效果
image.png
實(shí)際過程中除非需要通過傳入的參數(shù)組件來判斷性地去修改一些屬性噪馏,否則沒有必要使用繼承方式高階組件去操縱props麦到。
2. 操縱生命周期函數(shù)
繼承方式的高階組件需要修改生命周期函數(shù)時(shí)直接在高階組件內(nèi)重寫生命周期函數(shù)即可,它會覆蓋掉參數(shù)組件的生命周期函數(shù)欠肾。
從上面可以看出來瓶颠,代理方式的高階組件要優(yōu)于繼承方式的高階組件,所以優(yōu)先使用代理方式的高階組件刺桃。
三粹淋、修改顯示的組件名
打開組件調(diào)試工具,可以看到組件B和組件C重名了瑟慈,都是A桃移,如果組件特別多的話,調(diào)試起來會很麻煩葛碧。
修改方法
這時(shí)候需要用到 react 類里面的靜態(tài)屬性 displayName借杰,用于設(shè)置顯示的組件名稱。
A.js
import React, { Component } from 'react';
function hocA(Wrapper) {
return class A extends Component {
// 設(shè)置顯示高階組件顯示名稱
static displayName = `Box${getDisplayName(Wrapper)}`;
render() {
return (
<div className="box">
<header className="head">頭部</header>
<div className="content">
<Wrapper />
</div>
</div>
)
}
}
};
function getDisplayName(Wrapper) {
return Wrapper.displayName || Wrapper.name || 'defaultName'
}
export default hocA;
-
效果
image.png