react高階組件

一個高階組件就是一個函數,這個函數接受一個組件作為輸入,然后返回一個新的組件作為結果,而且,返回的新組件擁有了輸入組件所不具有的功能。我們可以這么打比方,每個組件最后都返回了一個jsx,而jsx實質上一個對象,相當于我們傳入一個對象,最后返回了一個新對象,它具有參數對象不具有的功能

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

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

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

其次,修改現有 React 組件的行為 有些現成 React 組件并不是開者自己開發的,來自于第3方,或者,即使是我們自己開發的,但是我們不想去觸碰這些組件的內部邏輯,這時候高階組件有了用武之地 通過一個獨立于原有組件的函數,可以產生新的組件,對原有組件沒有任何侵害。

根據返回的新組件和傳人組件參數的關系,高階組件的實現方式可以分為兩大類:

  • 代理方式的高階組件

  • 繼承方式的高階組件

代理方式的高階組件

上面的 removeUserProp 例子就是一個代理方式的高階組件,特點是返回的新組件類直接繼承自 React. Component 新組件扮演的角色是傳入參數組件的一個“代理”,在新組建的 render 函數中,把被包裹組件渲染出來,除了高階組件自己要做的工作,其余功能全都轉手給了被包裹的組件。

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

  • 操縱 prop

  • 訪問 ref

  • 抽取狀態

  • 包裝組件

  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. 抽取狀態
  const doNothing = () => ({})
  function connect(mapStateToProps=doNothing,mapDispatchToProps=doNothing){
    return function(WrapperComponent){
      class HOCComponent extends React.Component{
        //定義聲明周期函數
        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>
        }
      }
    }
  }

繼承方式的高階組件

繼承方式的高階組件采用繼承關系關聯作為參數的組件和返回的組件,假如傳入的組件參數是 WrComponeappednt ,那么返回的組件就直接繼承自 WrappedComponent

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

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

  • 操縱 prop

  • 操縱生命周期函數

  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. 操縱生命周期函數:修改參數組件的生命周期
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
    }
  }
}

以函數為子組件

高階組件并不是唯一可用于提高 React 組件代碼重用的方法 在上 節的介紹中可以體會到,高階組件擴展現有組件功能的方式主要是通過 props ,增加 props 或者減少props ,或者修改原有的 props 以代理方式的高階組件為例,新產生的組件和原有的組件說到底是兩個組件,是父子關系,而兩個 React 組件之間通信的方式自然是 props 因為每個組件都應該通過 propTypes 聲明自己所支持的 props 高階組件利用原組件的 props擴充功能,在靜態代碼檢查上也占優勢>

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

“以函數為子組件”的模式就是為了克服高階組件的這種局限而生的 在這種模式下,實現代碼重用的不是一個函數,而是一個真正的 React 組件,這樣的 React 組件有個特點,要求必須有子組件的存在,而且這個子組件必須是一個函數 在組件實例的生命周期函數中, this props children 引用的就是子組件, render 函數會直接this.props.children當做函數來調用,得到的結果就可以作為 render 返回結果的一部分

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>

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容

  • 在目前的前端社區,『推崇組合,不推薦繼承(prefer composition than inheritance)...
    Wenliang閱讀 77,705評論 16 124
  • 高階組件是對既有組件進行包裝,以增強既有組件的功能。其核心實現是一個無狀態組件(函數),接收另一個組件作為參數,然...
    柏丘君閱讀 3,099評論 0 6
  • React高階組件探究 在使用React構建項目的過程中,經常會碰到在不同的組件中需要用到相同功能的情況。不過我們...
    緋色流火閱讀 2,578評論 4 19
  • 什么是高階組件? high-order-function(高階函數)相信大多數開發者來說都熟悉,即接受函數作為參數...
    哇塞田閱讀 9,114評論 9 23
  • 不是何時開始,樓道上,多了幾只貓,自從有了貓,老鼠也逃得不見蹤影。早些年被老鼠困擾多時,現在能滅跡老鼠,對于貓這個...
    七月風閱讀 388評論 4 4