不積跬步--使用react 高階組件的正確姿勢

知識(shí)的學(xué)習(xí)一定要誠信于自己。

高階函數(shù)的基本概念

1.函數(shù)可以作為參數(shù)被傳遞,如:

setTimeout(()=>{
    console.log(1)
},1000)

2.函數(shù)可以作為返回值被輸出,如:

function foo(x){
    return function(){
        return x; 
    }
}

高階組件的基本概念(High Order Component,HOC)

1.高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新的組件
2.高階組件是一個(gè)函數(shù),并不是一個(gè)組件

高階組件就是一個(gè)簡單的函數(shù),它會(huì)接收一個(gè)React組件作為參數(shù),然后返回另一個(gè)React組件。
通常,HOC會(huì)使用一個(gè)能夠維護(hù)State或包含若干功能的類來包裝輸入的組件。高階組件是組件之間功能復(fù)用的最佳方式。

高階組件.png

高階組件的實(shí)現(xiàn)方式

屬性代理

我們可以看到下面高階組件最簡單的實(shí)現(xiàn)方式。我們把一個(gè)組件作為參數(shù)傳入到我們的函數(shù)里,在我們的函數(shù)里我們定義了一個(gè)類繼承了React.Component,然后通過render的方法中通過return把傳入的組件返回。這樣我們就可以代理所有傳入的props,并且決定如何渲染他們。實(shí)際上高階組件就是原組件的父組件。這就是屬性代理的實(shí)現(xiàn)方式。

function ComponentHOC(WrappedComponent) {
    return class extends React.Component {
       render() {
           return <WrappedComponent {...this.props}/>
       }
    }
}

對比原生組件增強(qiáng)的項(xiàng):

  • 可操作所有穿傳入的props
  • 可操作組件的生命周期
  • 可操作組件的static方法
  • 獲取refs

反向繼承

反向繼承是返回一個(gè)組件,這個(gè)組件繼承傳入的組件,然后在render中調(diào)用繼承組件的render方法。由于繼承于原組件,能通過this訪問到原組件的生命周期,props,state,render等,相比屬性代理它能操作更多的屬性。

function ConsoleHOC(WrappedComponent) {
    return class extends WrappedComponent {
        render(){
            return super.render();
        }
    }
}

對比原生組件增強(qiáng)的項(xiàng):

  • 可操作所有傳入的props
  • 可操作組件的生命周期
  • 可操作組件的static方法
  • 獲取refs
  • 可操作state
  • 可以渲染劫持

HOC可以實(shí)現(xiàn)什么功能

組合渲染--可使用任何其他的組件和原組件進(jìn)行組合渲染,達(dá)到樣式,布局復(fù)用等效果。

我們用兩種實(shí)現(xiàn)方式分別實(shí)現(xiàn)以下

//屬性代理的實(shí)現(xiàn)方式
function styleHOC(WrappedComponent){
    return class extends Component{
        render(){
            return (
                <div>
                    <div className="title">{this.props.title}</div>
                    <WrappedComponent {...this.props}/>
                </div>
            )
        }
    }
}
//反向繼承的實(shí)現(xiàn)方式
function styleHOC(WrappedComponent){
    return class extends WrappedComponent{
        render(){
            return (
                <div>
                    <div className="title">{this.props.title}</div>
                    {super.render()}
                 </div>
            )
        }
    }
}

條件渲染--根據(jù)特定的屬性決定原組件是否渲染

//通過屬性代理的方式實(shí)現(xiàn)
function visibleHOC(WrappedComponent){
    return class extends Component {
        render(){
            if(this.props.visible === false) return null
            return <WrappedComponent {...this.props}/>
        }
    }
}
//通過反向繼承的方式實(shí)現(xiàn)
function visibleHOC(WrappedComponent){
    return class extends WrappedComponent{
        render(){
            if(this.props.visible === false){
                return null
            }else{
                return super.render()
            }
        }
    }
}

操作Props--可以對傳入的props進(jìn)程增加,修改,刪除或者根據(jù)特定的Props進(jìn)行特殊的操作:

//通過屬性代理的方式實(shí)現(xiàn)
function proxyHOC(WrappedComponent){
    return class extends Component{
        render(){
            const newProps = {...this.props,user:'張三'};
            return <WrappedComponent {...newProps}/>
        }
    }
}
//通過反向繼承實(shí)現(xiàn)
function proxyHOC(WrappedComponent){
    return class extends WrappedComponent{
        render(){
             const newProps = {...this.props,user:'張三'};
             //doSomething...
             return super.render();
        }
    }
}

獲取refs

我們通過獲取原組件的ref來操作原組件的一些方法。關(guān)于獲取ref可以參考官網(wǎng)的Refs & DOM這一章節(jié)。從官網(wǎng)中我們知道,現(xiàn)在它推薦使用兩種方式來獲取refs

第一種是使用:React.createRef()

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

第二種是使用:回調(diào)

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = null;
    this.setRef=element=>{
        this.myRef = element;
    }
  }
  render() {
    return <div ref={this.setRef} />;
  }
}

我們的高階函數(shù)同樣可以使用上面的方式。

//通過屬性代理實(shí)現(xiàn)
function refHOC(WrappedComponent) {
  return class extends Component {
    this.wapperRef = null;
    componentDidMount() {
      this.wapperRef.log()
    }
    render() {
      return <WrappedComponent {...this.props} ref={ref => { this.wapperRef = ref }} />;
    }
  }
}

狀態(tài)管理

將原組件的狀態(tài)提取到HOC中進(jìn)行管理,如下面的代碼。我們將Inputvalue提取到HOC中進(jìn)行管理,使它變成受控組件,同時(shí)不影響它使用onChange方法進(jìn)行一些其他操作。

//通過屬性代理的方式實(shí)現(xiàn)
function proxyHoc(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = { value: '' };
    }

    onChange = (event) => {
      const { onChange } = this.props;
      this.setState({
        value: event.target.value,
      }, () => {
        if(typeof onChange ==='function'){
          onChange(event);
        }
      })
    }

    render() {
      const newProps = {
        value: this.state.value,
        onChange: this.onChange,
      }
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  }
}

class HOC extends Component {
  render() {
    return <input {...this.props}></input>
  }
}

export default proxyHoc(HOC);

獲取state

上面的方式實(shí)現(xiàn)上是高階組件也就是父組件把它自己的實(shí)現(xiàn)綁定到子組件的input組件上面了。并沒有直接去操作state.而通過反向繼承,我們可以直接操作原組件的state.但是并不推薦直接操作或添加state.容易出事兒。

//通過反向繼承實(shí)現(xiàn)
function debugHOC(WrappedComponent){
    return class extends WrappedComponent{
        render(){
            console.log('props',this.props);
            console.log('props',this.state);
            return super.render();
        }
    }
}

上面是一個(gè)簡單的debug的高階組件,我們可以寫很多類似這樣的調(diào)試組件,在使用的時(shí)候直接@debug.多爽。

如何使用HOC

HOC實(shí)際上就是一個(gè)函數(shù),所以我們可以把它當(dāng)成函數(shù)那樣使用:

function debugHOC(WrappedComponent){
    return class extends WrappedComponent{
        render(){
            console.log('props',this.props);
            console.log('props',this.state);
            return super.render();
        }
    }
}

class MyComponent extends Component{
    render(){
        return (<span>原組件</span>)
    }
}

export default debugHOC(MyComponent);

Decorators 裝飾器模式

我們可以借助ES7為我們提供的Decorators來讓我們的寫法變的更加優(yōu)雅:

@logger
@visible
@style
class Input extends Component {
  // ...
}

DecoratorsES7的一個(gè)提案,還沒有被標(biāo)準(zhǔn)化,但目前Babel轉(zhuǎn)碼器已經(jīng)支持,我們需要提前配置babel-plugin-transform-decorators-legacy

"plugins": ["transform-decorators-legacy"]

還可以結(jié)合compose函數(shù)使用:

const hoc = compose(logger,visible,style);
@hoc
class Input extends Component {
    //...
}

我們暫時(shí)更新到這里...

參考文檔:
官網(wǎng)高階組件
【React深入】從Mixin到HOC再到Hook

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

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

  • React進(jìn)階之高階組件 前言 本文代碼淺顯易懂,思想深入實(shí)用。此屬于react進(jìn)階用法,如果你還不了解react...
    流動(dòng)碼文閱讀 1,232評(píng)論 0 1
  • 前言 學(xué)習(xí)react已經(jīng)有一段時(shí)間了,期間在閱讀官方文檔的基礎(chǔ)上也看了不少文章,但感覺對很多東西的理解還是不夠深刻...
    Srtian閱讀 1,760評(píng)論 0 7
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,183評(píng)論 2 35
  • 前言 Ract16后,想去官網(wǎng)看看有啥新的特性,無意間發(fā)現(xiàn)官網(wǎng)支持簡體中文了,我是有多久沒看了(以前學(xué)習(xí)害得我看繁...
    Kevin丶CK閱讀 1,298評(píng)論 0 1
  • 在多個(gè)不同的組件中需要用到相同的功能,這個(gè)解決方法,通常有Mixin和高階組件。Mixin方法例如: 但是由于Mi...
    小魚小蝦小海洋閱讀 891評(píng)論 0 3

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