R-6.React高階組件詳解

看題目感覺好高級的樣子,千萬不要被名字嚇到,它一點(diǎn)都不高深。
按照慣例先上圖,這一章的概覽:

高階組件

1.從高階函數(shù)說起

維基百科對高階函數(shù)的定義:

數(shù)學(xué)計算機(jī)科學(xué)中,高階函數(shù)是至少滿足下列一個條件的函數(shù)

  • 接受一個或多個函數(shù)作為輸入
  • 輸出一個函數(shù)

是不是很簡單?滿足任一條件一句話說就是接受或者返回一個函數(shù)的函數(shù)就是高階函數(shù)。
舉個例子

    funA(){
        return funB(){};
    }

funA 就是一個高階函數(shù)。
順便提一下圖上的高階函數(shù)對應(yīng)的三個實現(xiàn),也是面試經(jīng)常問道的,這里簡單說一下。

1).函數(shù)回調(diào)

我們開發(fā)最長用的封裝網(wǎng)絡(luò)請求是一個高階函數(shù)。

    function funRequest(params){
        return Http.post(params.URL,params.data);// 這里實際返回的是一個promise
    }

2).柯里化

是把接受多個參數(shù)函數(shù)變換成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)

其實就是 fun(a,b,c) -------柯里化-----> fun(a)(b)(c);
舉個例子:

function add(a,b,c) {
    return (a + b + c);// 返回三個參數(shù)的和
}

// 把上述函數(shù)柯里化(這是一個簡單實現(xiàn),有興趣的想想怎么實現(xiàn)任意個數(shù)的求和)
function addH(a) {
    return function (b) {
       return function (c) {
           return (a + b + c);
       }
    }
}

console.log(add(1,2,3));
console.log(addH(1)(2)(3));

3).函數(shù)節(jié)流

這個也不高深,就是平時我們頁面加載過程中會有上拉頻繁,加載數(shù)據(jù)會出現(xiàn)之前的請求還沒有返回就又加載新的內(nèi)容,不停的發(fā)送請求。這時我們就會進(jìn)行節(jié)流。
偽代碼:

   function request() {
    let start = true;
    return function (params) {
        if(start) {
            start = false;
            Http.post(params.URL, params.data).then(res => {
                start = true;
            })
        }
    }
}

let startRequest = request();

if('觸發(fā)請求'){
    startRequest({URL:'sslslssllsl',data:{}});
}

2.高階組件(order-hight-component)

高階組件跟高階函數(shù)極度相似,把函數(shù)的傳入值和返回值從函數(shù)變成組件,那么這個函數(shù)就是高階組件。
WrapperComponent參數(shù)是一個組件,那么下面函數(shù)HocComp1和HocComp2都是高階組件

    const HocComp1 = WrapperComponent => 
         class extends Component{// 繼承Component
        
            render(){
                return (
                    <div>
                        HOC
                        <WrapperComponent />
                    </div>
                )
            }
         }
    
    const HocComp2 = WrapperComponent =>
        class extends WrapperComponent{// 繼承的是傳入的組件
    
            render(){
                const oldElements = super.render();
                return (
                    <div>
                        HOC
                        {oldElements}
                    </div>
                )
            }
        }

上面就是最簡單的創(chuàng)建的兩個高階組件,功能是一樣的在元組件的頂上加上“HOC”。

  • 繼承Component的是屬于屬性代理方式創(chuàng)建的高階組件;
  • 繼承傳入組件的屬于反向繼承方式創(chuàng)建的高階組件

1).屬性代理

屬性代理顧名思義,就是替代的意思。高階組件替?zhèn)魅虢M件管理控制props里面一切屬性,管理控制包括增,刪,改,查。同時他自身還有自身的狀態(tài),即state,來強(qiáng)化傳入組件。打個比方,傳入組件是畫一個圓,其中只有一個props屬性半徑radius。那我們在高階組件中就可以隨意操作這個屬性值,可大可小,還可以為props增加新的屬性,比如增加一個color屬性,表示圓的顏色;在組件外層加一個背景,美化傳入組件。這個比方由大家去實現(xiàn)。
這里舉一個稍微比這個難一點(diǎn)點(diǎn)的例子,受控的輸入框。輸入框組件的要求很簡單,就是輸入框中一直要有“輸入:”這兩個字和冒號。
分析:根據(jù)什么是受控組件,第一我們要通過state控制input的value屬性。
第二我們要監(jiān)控input輸入值得變化,每當(dāng)變化是我們拿到最新輸入值,然后在前面拼接上“輸入:”,設(shè)置一個state就可以了。所以需要onChange監(jiān)聽。
組件代碼如下:

    class ControlInput extends Component{
        // 一個受控組件,通過屬性代理的方式,把控制邏輯放進(jìn)高階組件中。
        render(){
            const { value , eventOnChange} = this.props;
            return (
                <input value={value} {...eventOnChange}/>
            )
        }
    }

注意這里沒有任何邏輯,屬性代理嘛,邏輯當(dāng)然都在高階組件中啦。
高階組件如下:

        import React,{ Component } from 'react';
        import '../../style/higherOrderComponent/higherOrderComponent.scss';
        
        const AttributeAgentHigherOrderComponent2 = (BaseComponent) =>
             class extends Component{
        
                constructor(props){
                    super(props);
                    this.state = {
                        value:this.props.initValue || '',
                    }
                }
        
                onValueChange = (event) => {
                    let value = event.target.value.toString();
                    // 這句最直觀的體現(xiàn)什么是受控(要什么值顯示什么值)
                    value = `輸入:${value === '輸入' ? '' : value.replace('輸入:','')}`;
                    this.setState({value:value});
                }
        
                render(){
                    const { value } = this.state;
                    const newProps = {
                      value: value,// input 的value屬性
                      eventOnChange:{
                          onChange: this.onValueChange,// input的onChange監(jiān)聽,方法在高階組件內(nèi)
                      },
                    }
                    const props = Object.assign({},this.props,newProps);// 合成最新的props傳給傳入組件
                    return (
                        <BaseComponent {...props}/>
                    )
                }
        
             }
        
        export default AttributeAgentHigherOrderComponent2;

怎么用的呢,在導(dǎo)出ControlInput組件的地方調(diào)用即可

export default AttributeAgentHigherOrderComponent2(ControlInput);

也可以使用ES6注解方式:


Es6注解方式

2).反向繼承

屬性代理方式在高階組件中返回的組件繼承的是Component,而反向繼承則是繼承的傳入組件,根據(jù)繼承的特性,繼承可獲取父類的所有靜態(tài)資源,非私有屬性和方法,且根據(jù)情況可對原方法進(jìn)行重寫。所以反向繼承的方式也可以操作傳入組件的props以及state。還有一個更重的就是反向繼承可以進(jìn)行渲染劫持。

我們來句一個簡單的例子,還是一個輸入框,要求

  • 輸入框中有值時就出現(xiàn)提交按鈕,沒有值時則消失。

  • 提交按鈕可用
    按照組件開發(fā),不用高階組件完全可以寫,還很簡單。但是現(xiàn)在我們就用高階組件寫,看有什么區(qū)別。先寫傳入組件:

          class ReverseInput extends Component{
      
          constructor(props){
              super(props);
              this.state = {
                  value:''
              }
          }
          // 處理提交動作。定義了方法沒有方法實體
          toSubmit = () => {}
          // 處理輸入值變化動作。定義了方法沒有方法實體
          valueChange = (eve) => {}
      
          render(){
              const { value } = this.state;
              return (
                  <div>
                      <input onChange={this.valueChange} value={value}/>
                      <button onClick={this.toSubmit}>提交</button>
                  </div>
              )
          }
      }
    

上面就是將要被繼承的組件,里面有方法,但是卻沒有方法實體。這就是反向繼承可以在高階組件中進(jìn)行方法的重寫。注意組件中是有提交按鈕的,我們要在高階組件中進(jìn)行控制顯示和隱藏,使用的就是渲染劫持。

    const ReverseInherit1 = BaseComponent =>
        class extends BaseComponent{ // 繼承傳入組件
             // 在這里定義監(jiān)聽value值變化的函數(shù)
            valueChange = (eve) => {
                console.log(eve.target.value);
                this.setState({value:eve.target.value})
            }
             // 在這里重寫提交的函數(shù)
            toSubmit = () => {
                alert(`您要提交的值是:${this.state.value}`);
            }
    
            render(){
                const { value } = this.state;
                const superEle =  super.render();// 拿到父組件的要渲染的結(jié)構(gòu)對象,做渲染劫持的關(guān)鍵
                const newElement = React.cloneElement(superEle,this.props,superEle.props.children);
                if(value){// 如果value有值就不做任何處理返回父組件的render
                    return (
                        super.render()
                    )
                }else{// value 有值則對原來的結(jié)構(gòu)進(jìn)行調(diào)整
                    newElement.props.children.splice(1,1);
                    return (newElement)
                }
                console.log(superEle);
            }
        }

用法跟上一個屬性代理的一致。

是不是感覺高階組件也不過如此?實際上難就難在去抽象組件邏輯,針對不同的需求。我門項目上就有一個問題比如權(quán)限相關(guān)的,所有組件中都有于某一權(quán)限相關(guān)的按鈕或者其他內(nèi)容,這些按鈕和內(nèi)容是和權(quán)限相關(guān)的,權(quán)限不足的人不能看到,如果每個人寫組件的時候都在自己組件里判斷,然后進(jìn)行是否顯示,這就造成一個問題,如果以后權(quán)限變動了,就要改很多處,這是比較麻煩的。但是高階組件很好的解決了這個問題。只要在你寫的組件中進(jìn)行權(quán)限控制顯示的內(nèi)容上加一個標(biāo)記,比如ref=‘xxxPower’等。我就可以讓你組件通過我的高階組件時對你的加了權(quán)限標(biāo)記的內(nèi)容進(jìn)行顯示和隱藏,所有組件都可以。

這個例子寫在下面的工程源碼里面了。運(yùn)行后就是菜單 “HOC-反向繼承2.1”.源碼位置在powerButton文件夾。
工程源碼地址,點(diǎn)擊這里

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

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

  • 在目前的前端社區(qū),『推崇組合,不推薦繼承(prefer composition than inheritance)...
    Wenliang閱讀 78,065評論 16 124
  • 前言 學(xué)習(xí)react已經(jīng)有一段時間了,期間在閱讀官方文檔的基礎(chǔ)上也看了不少文章,但感覺對很多東西的理解還是不夠深刻...
    Srtian閱讀 1,761評論 0 7
  • 高階組件是對既有組件進(jìn)行包裝,以增強(qiáng)既有組件的功能。其核心實現(xiàn)是一個無狀態(tài)組件(函數(shù)),接收另一個組件作為參數(shù),然...
    柏丘君閱讀 3,217評論 0 6
  • 本筆記基于React官方文檔,當(dāng)前React版本號為15.4.0。 1. 安裝 1.1 嘗試 開始之前可以先去co...
    Awey閱讀 7,934評論 14 128
  • React進(jìn)階之高階組件 前言 本文代碼淺顯易懂,思想深入實用。此屬于react進(jìn)階用法,如果你還不了解react...
    流動碼文閱讀 1,238評論 0 1

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