react高階組件

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

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

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

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

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

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

  • 代理方式的高階組件

  • 繼承方式的高階組件

代理方式的高階組件

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

代理方式的高階組件,可以應(yīng)用在下列場(chǎng)景中:

  • 操縱 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方法
        // 只是一個(gè)render函數(shù),不是一整個(gè)生命周期
        return super.render()
      }
    }  
  }

繼承方式的高階組件可以應(yīng)用于下列場(chǎng)景:

  • 操縱 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é)的介紹中可以體會(huì)到,高階組件擴(kuò)展現(xiàn)有組件功能的方式主要是通過 props ,增加 props 或者減少props ,或者修改原有的 props 以代理方式的高階組件為例,新產(chǎn)生的組件和原有的組件說到底是兩個(gè)組件,是父子關(guān)系,而兩個(gè) React 組件之間通信的方式自然是 props 因?yàn)槊總€(gè)組件都應(yīng)該通過 propTypes 聲明自己所支持的 props 高階組件利用原組件的 props擴(kuò)充功能,在靜態(tài)代碼檢查上也占優(yōu)勢(shì)>

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

“以函數(shù)為子組件”的模式就是為了克服高階組件的這種局限而生的 在這種模式下,實(shí)現(xiàn)代碼重用的不是一個(gè)函數(shù),而是一個(gè)真正的 React 組件,這樣的 React 組件有個(gè)特點(diǎn),要求必須有子組件的存在,而且這個(gè)子組件必須是一個(gè)函數(shù) 在組件實(shí)例的生命周期函數(shù)中, this props children 引用的就是子組件, render 函數(shù)會(huì)直接this.props.children當(dāng)做函數(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ù)為子組件這種方法非常合適做動(dòng)畫,作為子組件的函數(shù)主要專注于參數(shù)來渲染就可以了;但是它難以做性能優(yōu)化,因?yàn)樽咏M件是函數(shù),沒有生命周期,無法利用shouldComponentUpdate

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容