react高階組件

一個高階組件就是一個函數(shù)续担,這個函數(shù)接受一個組件作為輸入,然后返回一個新的組件作為結(jié)果活孩,而且物遇,返回的新組件擁有了輸入組件所不具有的功能。我們可以這么打比方憾儒,每個組件最后都返回了一個jsx询兴,而jsx實質(zhì)上一個對象,相當于我們傳入一個對象起趾,最后返回了一個新對象诗舰,它具有參數(shù)對象不具有的功能

// 刪除user這個props
  function removeUserProp(WrapperComponent){
    return class WrappingComponent extends React.Component{
      render(){
        const {user,...otherProps} = this.props
        return <WrapperComponent {...otherProps}>
      }
    }  
  }

定義高階組件的意義何在呢?

首先训裆,重用代碼 有時候很多 React 組件都需要公用同樣一個邏輯眶根,比如說 react-redux中容器組件的部分,沒有必要讓每個組件都實現(xiàn)一遍 shouldComponentUpdate 這些生命周期函數(shù)边琉,把這部分邏輯提取出來属百,利用高階組件的方式應用出去,就可以減少很多組件的重復代碼

其次变姨,修改現(xiàn)有 React 組件的行為 有些現(xiàn)成 React 組件并不是開者自己開發(fā)的族扰,來自于第3方,或者定欧,即使是我們自己開發(fā)的渔呵,但是我們不想去觸碰這些組件的內(nèi)部邏輯,這時候高階組件有了用武之地 通過一個獨立于原有組件的函數(shù)砍鸠,可以產(chǎn)生新的組件扩氢,對原有組件沒有任何侵害。

根據(jù)返回的新組件和傳人組件參數(shù)的關(guān)系睦番,高階組件的實現(xiàn)方式可以分為兩大類:

  • 代理方式的高階組件

  • 繼承方式的高階組件

代理方式的高階組件

上面的 removeUserProp 例子就是一個代理方式的高階組件类茂,特點是返回的新組件類直接繼承自 React. Component 新組件扮演的角色是傳入?yún)?shù)組件的一個“代理”,在新組建的 render 函數(shù)中托嚣,把被包裹組件渲染出來巩检,除了高階組件自己要做的工作,其余功能全都轉(zhuǎn)手給了被包裹的組件示启。

代理方式的高階組件兢哭,可以應用在下列場景中:

  • 操縱 prop

  • 訪問 ref

  • 抽取狀態(tài)

  • 包裝組件

  1. 操縱 prop
  // 添加新props
  const addNewProp = (WrapperComponent,newProps) => {
    return class WrappingComponent extends React.Component{
      render(){
        return <WrapperComponent {...this.props} {...newProps}>
      }
    }  
  }
  1. 訪問 ref
  // 獲取refs
  const refsHOC = (WrapperComponent) => {
    return class HOCComponent extends React.Component{
      constructor(){
        super(...arguments)
        this.linkRef = this.linkRef.bind(this)
      }
      linkRef(wrappedInstance){
        this._root = wrappedInstance
      }
      render(){
        const props = {...this.props,ref:this.linkRef}
        return <WrapperComponent {...props}>
      }
    }  
  }
  1. 抽取狀態(tài)
  const doNothing = () => ({})
  function connect(mapStateToProps=doNothing,mapDispatchToProps=doNothing){
    return function(WrapperComponent){
      class HOCComponent extends React.Component{
        //定義聲明周期函數(shù)
        constructor(){
          super(...arguments)
          this.onChange = this.onChange.bind(this)
          this.store = {}
        }
        componentDidMount(){
          this.context.store.subscribe(this.onChange)
        }
        componentWillUnMount(){
          this.context.store.unsubscribe(this.onChange)
        }
        onChange(){
          this.setState({})
        }

        render(){
          const store = this.context.store
          const newProps = {
            ...this.props,
            ...mapStateToProps(store.getState()),
            ...mapDispatchToProps(store.dispatch())
          }
          return <WrapperComponent {...newProps}>
        }
      }
      HOCComponent.contextTypes = {
        store:React.PropTypes.object
      }
      return HOCComponent
    }
  }

  function getDisplayName(WrappedComponent){
    return WrappedComponent.displayName || WrappedComponent.name || Component
  }

  1. 包裝組件
  const styleHOC = (WrappedComponent,style) => {
    return class HOCComponent extends React.Component{
      render(){
        return(){
          <div style={style}>
            <WrappedComponent {...this.props} />
          </div>
        }
      }
    }
  }

繼承方式的高階組件

繼承方式的高階組件采用繼承關(guān)系關(guān)聯(lián)作為參數(shù)的組件和返回的組件,假如傳入的組件參數(shù)是 WrComponeappednt 夫嗓,那么返回的組件就直接繼承自 WrappedComponent

function removeUserProp(WrapperComponent){
    //繼承于參數(shù)組件
    return class NewComponent extends WrapperComponent{
      render(){
        const {user,...otherProps} = this.props
        this.props = otherProps
        //調(diào)用WrapperComponent的render方法
        // 只是一個render函數(shù)迟螺,不是一整個生命周期
        return super.render()
      }
    }  
  }

繼承方式的高階組件可以應用于下列場景:

  • 操縱 prop

  • 操縱生命周期函數(shù)

  1. 操縱 prop
  const modifyPropsHOC = (WrappedComponent) => {
    return class NewComponent extends WrappedComponent{
      render(){
        const elements = super.render()
        const newStyle = {
          color:(elements && elements.type === 'div') ? 'red' : 'green'
        }
        const newProps = {...this.props,style:newStyle}
        return React.cloneElement(elements,newProps,elements.props.children)
      }
    }
  }
  1. 操縱生命周期函數(shù):修改參數(shù)組件的生命周期
const onlyForLoggedHOC = (WrappedComponent) => {
  return class NewComponent extends WrappedComponent{
    render(){
      if(this.props.loggedIn){
        return super.render()
      }else{
        return null
      }
    }
  }
}
const cacheHOC = (WrappedComponent) => {
  return class NewComponent extends WrappedComponent{
    shouldComponentUpdate(nextProps,nextState){
      return !nextProps.userCache
    }
  }
}

以函數(shù)為子組件

高階組件并不是唯一可用于提高 React 組件代碼重用的方法 在上 節(jié)的介紹中可以體會到冲秽,高階組件擴展現(xiàn)有組件功能的方式主要是通過 props ,增加 props 或者減少props 矩父,或者修改原有的 props 以代理方式的高階組件為例锉桑,新產(chǎn)生的組件和原有的組件說到底是兩個組件,是父子關(guān)系窍株,而兩個 React 組件之間通信的方式自然是 props 因為每個組件都應該通過 propTypes 聲明自己所支持的 props 高階組件利用原組件的 props擴充功能民轴,在靜態(tài)代碼檢查上也占優(yōu)勢>

但是,高階組件也有缺點球订,那就是對原組件的 props 有了固化的要求 也就是說后裸,能不能把一個高階組件作用于某個組件 ,要先看一下這個組件 是不是能夠接受高階組件傳過來的 props 冒滩,如果組件 并不支持這些 props 微驶,或者對這些 props 的命名有不同,或者使用方式不是預期的方式开睡,那也就沒有辦法應用這個高階組件因苹。

“以函數(shù)為子組件”的模式就是為了克服高階組件的這種局限而生的 在這種模式下,實現(xiàn)代碼重用的不是一個函數(shù)士八,而是一個真正的 React 組件容燕,這樣的 React 組件有個特點,要求必須有子組件的存在婚度,而且這個子組件必須是一個函數(shù) 在組件實例的生命周期函數(shù)中, this props children 引用的就是子組件官卡, render 函數(shù)會直接this.props.children當做函數(shù)來調(diào)用蝗茁,得到的結(jié)果就可以作為 render 返回結(jié)果的一部分

class CountDown extends React.Component{
  constructor(){
    super(...arguments)
    this.state = {count:this.props.startCount}
  }

  componentDidMount(){
    this.intervalHandle = setInterval(() => {
      const newCount = this.state.count - 1
      if(newCount >= 0){
        this.setState({count:newCount})
      }else{
        window.clearInterval(this.intervalHandle)
      }
    },1000)
  }

  componentWillUnMount(){
    if(this.intervalHandle){
      window.clearInterval(this.intervalHandle)
    }
  }

  render(){
    return this.props.children(this.state.count)
  }
}

CountDown.propTypes = {
  children:PropTypes.func.isRequired,
  startCount:PropTypes.number.isRequired
}
<CountDown>
  {
    (count) => <div>count</div>
  }
</CountDown>
<CountDown>
  {
    (count) => <div>{count > 0 ? count : 'happy new year'}</div>
  }
</CountDown>
<CountDown>
  {
    (count) => <Bomb countdown={count}>
  }
</CountDown>

以函數(shù)為子組件這種方法非常合適做動畫,作為子組件的函數(shù)主要專注于參數(shù)來渲染就可以了寻咒;但是它難以做性能優(yōu)化哮翘,因為子組件是函數(shù),沒有生命周期毛秘,無法利用shouldComponentUpdate

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饭寺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子叫挟,更是在濱河造成了極大的恐慌艰匙,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抹恳,死亡現(xiàn)場離奇詭異员凝,居然都是意外死亡,警方通過查閱死者的電腦和手機奋献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門健霹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旺上,“玉大人,你說我怎么就攤上這事糖埋⌒ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵瞳别,是天一觀的道長凌节。 經(jīng)常有香客問我,道長洒试,這世上最難降的妖魔是什么倍奢? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮垒棋,結(jié)果婚禮上卒煞,老公的妹妹穿的比我還像新娘。我一直安慰自己叼架,他們只是感情好畔裕,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乖订,像睡著了一般扮饶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乍构,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天甜无,我揣著相機與錄音,去河邊找鬼哥遮。 笑死岂丘,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的眠饮。 我是一名探鬼主播奥帘,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仪召!你這毒婦竟也來了寨蹋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扔茅,失蹤者是張志新(化名)和其女友劉穎已旧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咖摹,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡评姨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吐句。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡胁后,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嗦枢,到底是詐尸還是另有隱情攀芯,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布文虏,位于F島的核電站侣诺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏氧秘。R本人自食惡果不足惜年鸳,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丸相。 院中可真熱鬧搔确,春花似錦、人聲如沸灭忠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弛作。三九已至涕蜂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間映琳,已是汗流浹背机隙。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留刊头,地道東北人黍瞧。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像原杂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子您机,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

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

  • 在目前的前端社區(qū)穿肄,『推崇組合,不推薦繼承(prefer composition than inheritance)...
    Wenliang閱讀 77,681評論 16 125
  • 高階組件是對既有組件進行包裝际看,以增強既有組件的功能咸产。其核心實現(xiàn)是一個無狀態(tài)組件(函數(shù)),接收另一個組件作為參數(shù)仲闽,然...
    柏丘君閱讀 3,074評論 0 6
  • React高階組件探究 在使用React構(gòu)建項目的過程中脑溢,經(jīng)常會碰到在不同的組件中需要用到相同功能的情況。不過我們...
    緋色流火閱讀 2,571評論 4 19
  • 什么是高階組件? high-order-function(高階函數(shù))相信大多數(shù)開發(fā)者來說都熟悉,即接受函數(shù)作為參數(shù)...
    哇塞田閱讀 9,097評論 9 23
  • 不是何時開始,樓道上屑彻,多了幾只貓验庙,自從有了貓,老鼠也逃得不見蹤影社牲。早些年被老鼠困擾多時粪薛,現(xiàn)在能滅跡老鼠,對于貓這個...
    七月風閱讀 383評論 4 4