代理以及繼承方式的高階組件

上一篇文章介紹了簡單的高階組件實(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)想要的樣式锋边。

image.png


二皱坛、繼承方式的高階組件

采用繼承關(guān)聯(lián)作為參數(shù)的組件和返回的組件,假如傳入的組件參數(shù)是Wrapper豆巨,那么返回的組件就直接繼承自 Wrapper 剩辟。
和代理方式的高階組件的區(qū)別

image.png

  • 兩者繼承的類不同。代理方式的高階組件繼承自 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)試起來會很麻煩葛碧。


image.png

修改方法
這時(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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末进泼,一起剝皮案震驚了整個(gè)濱河市蔗衡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乳绕,老刑警劉巖绞惦,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洋措,居然都是意外死亡济蝉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門菠发,熙熙樓的掌柜王于貴愁眉苦臉地迎上來王滤,“玉大人,你說我怎么就攤上這事雷酪∈缙停” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵哥力,是天一觀的道長蔗怠。 經(jīng)常有香客問我墩弯,道長,這世上最難降的妖魔是什么寞射? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任渔工,我火速辦了婚禮,結(jié)果婚禮上桥温,老公的妹妹穿的比我還像新娘引矩。我一直安慰自己,他們只是感情好侵浸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布旺韭。 她就那樣靜靜地躺著熊楼,像睡著了一般厂庇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伯诬,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天澳腹,我揣著相機(jī)與錄音织盼,去河邊找鬼。 笑死酱塔,一個(gè)胖子當(dāng)著我的面吹牛沥邻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播羊娃,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唐全,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了迁沫?” 一聲冷哼從身側(cè)響起芦瘾,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎集畅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缅糟,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挺智,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窗宦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赦颇。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赴涵,靈堂內(nèi)的尸體忽然破棺而出媒怯,到底是詐尸還是另有隱情,我是刑警寧澤髓窜,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布扇苞,位于F島的核電站欺殿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鳖敷。R本人自食惡果不足惜脖苏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望定踱。 院中可真熱鬧棍潘,春花似錦、人聲如沸崖媚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽畅哑。三九已至鳍徽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敢课,已是汗流浹背阶祭。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留直秆,地道東北人濒募。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像圾结,于是被迫代替她去往敵國和親瑰剃。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • React進(jìn)階之高階組件 前言 本文代碼淺顯易懂筝野,思想深入實(shí)用晌姚。此屬于react進(jìn)階用法,如果你還不了解react...
    流動碼文閱讀 1,185評論 0 1
  • 在目前的前端社區(qū)歇竟,『推崇組合挥唠,不推薦繼承(prefer composition than inheritance)...
    Wenliang閱讀 77,677評論 16 125
  • 前言 學(xué)習(xí)react已經(jīng)有一段時(shí)間了盅安,期間在閱讀官方文檔的基礎(chǔ)上也看了不少文章唤锉,但感覺對很多東西的理解還是不夠深刻...
    Srtian閱讀 1,657評論 0 7
  • 前端開發(fā)面試題 面試題目: 根據(jù)你的等級和職位的變化,入門級到專家級别瞭,廣度和深度都會有所增加窿祥。 題目類型: 理論知...
    怡寶丶閱讀 2,582評論 0 7
  • 高階組件是react應(yīng)用中很重要的一部分,最大的特點(diǎn)就是重用組件邏輯蝙寨。它并不是由React API定義出來的功能晒衩,...
    叫我蘇軾好嗎閱讀 900評論 0 0