18.React學(xué)習(xí)筆記.高階組件

##一. 高階組件##

1.1 認(rèn)識(shí)高階組件###

高階函數(shù)####

維基百科定義,至少滿(mǎn)足以下條件之一:

  • 接受一個(gè)或多個(gè)函數(shù)作為輸入坯门;
  • 輸出一個(gè)函數(shù)。
    JS中常見(jiàn)的filter,map把沼,reduce都是高階函數(shù)。

那什么是高階組件呢吁伺?

  • 全名為Higher-Order Components饮睬,簡(jiǎn)稱(chēng)HOC。
  • 官方定義篮奄,HOC是參數(shù)為組件捆愁,返回值為新組件的函數(shù)

也就是說(shuō):

  • 高階函數(shù)割去,本身,不是組件牙瓢,是函數(shù)劫拗。
  • 其參數(shù)是,組件矾克,返回值也是組件页慷。
const EnhancedComponent = higherOrderComponent(WrappedComponent);

注意:給高階組件傳遞屬性時(shí),需要在高階組件內(nèi)部進(jìn)行相應(yīng)接收胁附。

import React, { PureComponent } from 'react'
class App extends PureComponent {
  render() {
    return (
      <div>
        App:{this.props.name}
      </div>
    )
  }
}
function enhanceComponent(WrappedComponent) {
  return class extends PureComponent {
    render() {
      return <WrappedComponent {...this.props}/>
    }
  }
}
const EnhanceComponent = enhanceComponent(App);
// 導(dǎo)出的為自定義的高階組件
export default EnhanceComponent;

// index.js中的DOM渲染
ReactDOM.render(<App name="why"/>,document.getElementById('root'));

**補(bǔ)充:

  • 高階組件并不是React API的一部分酒繁,它是基于React的組合特性而形成的設(shè)計(jì)模式;
  • 在一些第三方庫(kù)中很常見(jiàn):如redux中的connect控妻;react-router中的withRouter州袒;
    **

1.2 高階組件的使用###

1.2.1 props的增強(qiáng)####

  1. 不修改原有代碼的情況,添加新的props
    ``
    import React, { PureComponent } from 'react'
    // 定義一個(gè)高階組件
    function enhanceRegionProps(WrappedComponent) {
    return props => {
    return <WrappedComponent {...props} region="中國(guó)" />
    }
    }
    class Home extends PureComponent {
    render() {
    return <h2>Home:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}}</h2>
    }
    }
    class About extends PureComponent {
    render() {
    return <h2>About:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}}</h2>
    }
    }
    const EnhanceHome = enhanceRegionProps(Home);
    const EnhanceAbout = enhanceRegionProps(About);

class App extends PureComponent {
render() {
return (
<div>
App:
<EnhanceHome nickname="coderwwq" level={90}/>
<EnhanceAbout nickname="kobe" level={99}/>
</div>
)
}
}
export default App;


2. 利用高階組件來(lái)共享Context
利用withUser弓候,**劫持props**

import React, { PureComponent, createContext } from 'react'

// 定義一個(gè)高階組件
function withUser(WrappedComponent) {
return props => {
return (
<UserContext.Consumer>
{
user => {
return <WrappedComponent {...props} {...user} />
}
}
</UserContext.Consumer>
)
}
}

// 創(chuàng)建Context
const UserContext = createContext({
nickname: "默認(rèn)",
level: -1,
region: "中國(guó)"
})

class Home extends PureComponent {
render() {
return <h2>Home:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About:{昵稱(chēng):${this.props.nickname} 等級(jí):${this.props.level} 區(qū)域:${this.props.region}}</h2>
}
}

const UserHome = withUser(Home);
const UserAbout = withUser(About);

class App extends PureComponent {
render() {
return (
<div>
App:
<UserContext.Provider value={{ nickname: "wwq", level: 18, region: "中國(guó)" }}>
<UserHome />
<UserAbout />
</UserContext.Provider>
</div>
)
}
}
export default App;



####**1.2.2 渲染判斷鑒權(quán)**####
- 某些頁(yè)面必須用戶(hù)登陸成功才能進(jìn)入郎哭;
- 如果用戶(hù)沒(méi)有登陸成功,那么直接跳轉(zhuǎn)到登陸頁(yè)面菇存;
- **劫持jsx**

import React, { PureComponent } from 'react'

class LoginPage extends PureComponent{
render() {
return <h2>LoginPage</h2>
}
}
function withAuth(WrappedComponent) {
const NewCpn = props => {
const {isLogin} = props;
if (isLogin) {
return <WrappedComponent {...props}/>
} else {
return <LoginPage/>
}
}
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
// 購(gòu)物車(chē)組件
class CartPage extends PureComponent {
render() {
return <h2>CartPage</h2>
}
}
const AuthCartPage = withAuth(CartPage);

export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={false}/>
</div>
)
}
}


####**1.2.3 生命周期劫持**####

import React, { PureComponent } from 'react'

class Home extends PureComponent {
// 即將渲染獲取一個(gè)時(shí)間
// 該API即將廢棄
componentWillMount(){
this.beginTime = Date.now();
}

render() {
return <h2>Home:</h2>
}
// 渲染完成再獲取一個(gè)時(shí)間
componentDidMount(){
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(Home渲染時(shí)間:${interval});

}
}
class About extends PureComponent {
render() {
return <h2>About:</h2>
}
}

export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
<About/>
</div>
)
}
}


- 上面代碼進(jìn)行高階組件封裝

import React, { PureComponent } from 'react'
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
componentWillMount(){
this.beginTime = Date.now();
}
componentDidMount(){
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(${WrappedComponent.name}渲染時(shí)間:${interval});
}
render() {
return <WrappedComponent {...this.props}/>
}
}
}
class Home extends PureComponent {
render() {
return <h2>Home:</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About:</h2>
}
}
const TimeHome = withRenderTime(Home);
const TimeAbout = withRenderTime(About);

export default class App extends PureComponent {
render() {
return (
<div>
<TimeHome/>
<TimeAbout/>
</div>
)
}
}


**注意:不要?jiǎng)?chuàng)建自己的組件基類(lèi)夸研,代碼重用的主要方式是組合而不是繼承。**
###**1.3 高階函數(shù)的意義**###
- 針對(duì)某些代碼進(jìn)行優(yōu)雅的處理依鸥。
- 不修改原有代碼的情況下亥至,添加新的props

**缺陷**:
- HOC需要在原組件上包裹或者嵌套,如果大量適用HOC贱迟,將會(huì)產(chǎn)生非常多的嵌套姐扮,調(diào)試?yán)щy。
- HOC可以劫持props衣吠,在不遵守約定的情況下可能造成沖突茶敏。

**##二. 組件補(bǔ)充##**
- 函數(shù)組件不能用ref。
- ref不是屬性缚俏,交由React內(nèi)部管理惊搏。 

- 獲取函數(shù)組件元素,用forwardRef高階組件袍榆。

###**2.1 ref轉(zhuǎn)發(fā)**###
- forwardRef給原函數(shù)組件進(jìn)行增強(qiáng)胀屿,加入一個(gè)ref屬性。
- 在hooks中會(huì)更簡(jiǎn)單包雀,后話(huà)宿崭。

import React, { PureComponent, createRef, forwardRef } from 'react'

class Home extends PureComponent {
render() {
return <h2>Home</h2>
}
}
// 已經(jīng)封裝好的高階組件
const Profile = forwardRef(function(props, ref){
console.log(props.name);

return <h2 ref={ref}>Profile</h2>
})
export default class App extends PureComponent {
constructor(props){
super(props);
this.titleRef = createRef();
this.homeRef = createRef();
this.profileRef = createRef();
}
render() {
return (
<div>
<h2 ref={this.titleRef}>Hello World</h2>

    <Home ref={this.homeRef}/>

    <Profile ref={this.profileRef}/>
    
    <button onClick={e=>this.printRef()}>打印ref</button>
  </div>
)

}
printRef(){
console.log(this.titleRef.current);
console.log(this.homeRef.current);
console.log(this.profileRef.current);
}
}

###**2.2 Portals**###
- 某些情況下,我們希望渲染的內(nèi)容獨(dú)立于父組件才写,甚至是獨(dú)立于當(dāng)前掛載到的DOM元素中(默認(rèn)都是掛載到id為root的DOM元素上的)葡兑。
- 希望渲染的組件完全獨(dú)立奖蔓,例如,彈窗讹堤。

- 彈窗直接放到body中吆鹤。
- css完全居中。

Protal提供了一種將子節(jié)點(diǎn)渲染到存在于父組件以外的DOM節(jié)點(diǎn)的優(yōu)秀方案:
- 第一個(gè)參數(shù)child是任何可渲染的React子1元素洲守;
- 第二個(gè)參數(shù)container是一個(gè)DOM元素疑务。

####Modal組件案例:將子組件渲染到屏幕的中間位置####

import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
class Modal extends PureComponent {
render() {
return ReactDOM.createPortal(
this.props.children,
document.getElementById("modal")
)
}
}
class Home extends PureComponent {
render() {
return (
<div>
<h2>Home</h2>
<Modal>
<h2>Title</h2>
</Modal>
</div>
)
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home/>
</div>
)
}
}

###**2.3 Fragment**###

###**2.4 StrictMode嚴(yán)格模式**###
檢測(cè):
- 不安全的生命周期
- 使用過(guò)時(shí)的ref API
- 使用廢棄的findDOMNode方法
- 以外的副作用
- 過(guò)時(shí)的context API

####
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市梗醇,隨后出現(xiàn)的幾起案子知允,更是在濱河造成了極大的恐慌,老刑警劉巖叙谨,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件温鸽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡手负,警方通過(guò)查閱死者的電腦和手機(jī)涤垫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)竟终,“玉大人蝠猬,你說(shuō)我怎么就攤上這事『饫悖” “怎么了吱雏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵敦姻,是天一觀的道長(zhǎng)瘾境。 經(jīng)常有香客問(wèn)我,道長(zhǎng)镰惦,這世上最難降的妖魔是什么迷守? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮旺入,結(jié)果婚禮上兑凿,老公的妹妹穿的比我還像新娘。我一直安慰自己茵瘾,他們只是感情好礼华,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著拗秘,像睡著了一般圣絮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雕旨,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天扮匠,我揣著相機(jī)與錄音捧请,去河邊找鬼。 笑死棒搜,一個(gè)胖子當(dāng)著我的面吹牛疹蛉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播力麸,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼可款,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了克蚂?” 一聲冷哼從身側(cè)響起筑舅,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陨舱,沒(méi)想到半個(gè)月后翠拣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡游盲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年误墓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片益缎。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谜慌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出莺奔,到底是詐尸還是另有隱情欣范,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布令哟,位于F島的核電站恼琼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屏富。R本人自食惡果不足惜晴竞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望狠半。 院中可真熱鬧噩死,春花似錦、人聲如沸神年。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)已日。三九已至垛耳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艾扮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工既琴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泡嘴。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓甫恩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酌予。 傳聞我的和親對(duì)象是個(gè)殘疾皇子磺箕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348