国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

高階組件HOC - 小試牛刀

lookSomeone / 3544人閱讀

摘要:因為這個高階函數(shù),賦予了普通組件一種呼吸閃爍的能力記住這句話,圈起來重點考。其實,高階組件就是把一些通用的處理邏輯封裝在一個高階函數(shù)中,然后返回一個擁有這些邏輯的組件給你。

原文地址:https://github.com/SmallStoneSK/Blog/issues/6

1. 前言

老畢曾經(jīng)有過一句名言,叫作“國慶七天樂,Coding最快樂~”。所以在這漫漫七天長假,手癢了怎么辦?于是乎,就有了接下來的內(nèi)容。。。

2. 一個中心

今天要分享的內(nèi)容有關(guān)高階組件的使用。

雖然這類文章早已經(jīng)爛大街了,而且想必各位看官也是稔熟于心。因此,本文不會著重介紹一堆HOC的概念,而是通過兩個實實在在的實際例子來說明HOC的用法和強大之處。

3. 兩個例子 3.1 例子1:呼吸動畫

首先,我們來看第一個例子。喏,就是這個。

是滴,這個就是呼吸動畫(錄的動畫有點渣,請別在意。。。),想必大家在絕大多數(shù)的APP中都見過這種動畫,只不過我這畫的非常簡陋。在數(shù)據(jù)ready之前,這種一閃一閃的呼吸動畫可以有效地緩解用戶的等待心理。

這時,有人就要跳出來說了:“這還不簡單,創(chuàng)建個控制opacity的animation,再添加class不就好了。。?!笔堑?,在web的世界中,css animation有時真的可以為所欲為。但是我想說,在RN的世界里,只有Animated才真的好使。

不過話說回來,要用Animated來做這個呼吸動畫,的確也很簡單。代碼如下:

class BreathLoading extends React.PureComponent {

  componentWillMount() {
    this._initAnimation();
    this._playAnimation();
  }

  componentWillUnmount() {
    this._stopAnimation();
  }

  _initAnimation() {
    this.oritention = true;
    this.isAnimating = true;
    this.opacity = new Animated.Value(1);
  }

  _playAnimation() {
    Animated.timing(this.opacity, {
      isInteraction: false,
      duration: params.duration,
      toValue: this.oritention ? 0.2 : 1,
      easing: this.oritention ? Easing.in : Easing.easeOut
    }).start(() => {
      this.oritention = !this.oritention;
      this.isAnimating && this._playAnimation();
    });
  }

  _stopAnimation = () => this.isAnimating = false;

  render = () => ;

}

是的,僅二十幾行代碼我們就完成了一個簡單地呼吸動畫。但是問題來了,假如在你的業(yè)務(wù)需求中有5個、10個場景都需要用到這種呼吸動畫怎么辦?總不能復(fù)制5次、10次,然后修改它們的render方法吧?這也太蠢了。。。

有人會想到:“那就封裝一個組件唄。反正呼吸動畫的邏輯都是不變的,唯一在變的是渲染部分??梢酝ㄟ^props接收一個renderContent方法,將渲染的實際控制權(quán)交給調(diào)用方?!蹦蔷蛠砜纯创a吧:

class BreathLoading extends React.PureComponent {
  // ...省略
  render() {
    const {renderContent = () => {}} = this.props;
    return renderContent(this.opacity);
  }
}

相比較于一開始的例子,現(xiàn)在這個BreathLoading組件可以被復(fù)用,調(diào)用方只要關(guān)注自己渲染部分的內(nèi)容就可以了。但是說實話,個人在這個組件使用方式上總感覺有點不舒服,有一個不痛不癢的小問題。習慣上來說,在真正使用BreathLoading的時候,我們通常會寫出左下圖中的這種代碼。由于renderContent接收的是一個匿名函數(shù),因此當組件A render的時候,雖然BreathLoading是一個純組件,但是前后兩次接收的renderContent是兩個不同的函數(shù),還是會發(fā)起一次不必要的domDiff。那還不簡單,只要把renderContent中的內(nèi)容多帶帶抽成一個函數(shù)再傳進去不就好了(見右下圖)。

對溜,這個就是我剛才說的不爽的地方。好端端的一個Loading組件,封裝你也封裝了,憑啥我還要分兩步才能使用。其實BB了那么久,你也知道埋了那么多的鋪墊,是時候HOC出場了。。。說來慚愧,在接觸HOC之前鄙人一直用的就是上面這種方法來封裝。。。直到用上了HOC之后,才發(fā)現(xiàn)真香真香。。。

在這里,我們要用到的是高階組件的代理模式。大家都知道,高階組件是一個接收參數(shù)、返回組件的函數(shù)而已。對于這個呼吸動畫的例子而言,我們來分析一下:

接收什么?當然是接收剛才renderContent返回的那個組件啦。

返回什么?當然是返回我們的BreathLoading組件啦。

OK,看完上面的兩句廢話之后,再來看下面的代碼。

export const WithLoading = (params = {duration: 600}) => WrappedComponent => class extends React.PureComponent {

  componentWillMount() {
    this._initAnimation();
    this._playAnimation();
  }

  componentWillUnmount() {
    this._stopAnimation();
  }

  _initAnimation() {
    this.oritention = true;
    this.isAnimating = true;
    this.opacity = new Animated.Value(1);
  }

  _playAnimation() {
    Animated.timing(this.opacity, {
      isInteraction: false,
      duration: params.duration,
      toValue: this.oritention ? 0.2 : 1,
      easing: this.oritention ? Easing.in : Easing.easeOut
    }).start(() => {
      this.oritention = !this.oritention;
      this.isAnimating && this._playAnimation();
    });
  }

  _stopAnimation = () => this.isAnimating = false;

  render = () => ;
};

看完上面的代碼之后,再回頭瞅瞅前面的那兩句話,是不是豁然開朗。仔細觀察WrappedComponent,我們發(fā)現(xiàn)opacity竟然以props的形式傳給了它。只要WrappedComponent拿到了關(guān)鍵的opacity,那豈不是想干什么就干什么來著,而且還沒有前面說的什么匿名函數(shù)和domDiff消耗問題。再配上decorator裝飾器,豈不是美滋滋?代碼如下:

@WithLoading()
class Test extends React.PureComponent {
  render() {
    const {opacity} = this.props;
    return (
      
        
          
          
        
        
          
          
        
      
    )
  }
}

相比之下,顯然高階組件的用法更勝一籌。以后不管要做成什么樣的呼吸動畫,只要加一個@withLoading就搞定了。因為這個高階函數(shù),賦予了普通組件一種呼吸閃爍的能力(記住這句話,圈起來重點考)。

3.2 例子2:多版本控制的組件

經(jīng)過上面的例子,我們初步感受到了高階組件的黑魔法。因為通過它,我們能讓一個組件擁有某種能力,能夠化腐朽為神奇。。。哦,吹過頭了。。。那我們來看第二個例子,也是業(yè)務(wù)需求中會遇到的場景。為啥?因為善變的產(chǎn)品經(jīng)常要改版,要做AB?。。?/p>

所謂多版本控制的組件,其實就是一個擁有相同功能的組件,由于產(chǎn)品的需求,經(jīng)歷了A版 -> B版 -> C版 -> D版。。。這無窮無盡的改版,有的換個皮膚,改個樣式,有的甚至改了交互。

或許對于一個簡單的小組件而言,每次改版只要重新創(chuàng)建一個新的組件就可以了。但是,如果對于一個頁面級別的Page組件呢?就像下面的這個組件一樣,作為容器組件,這個組件充斥著大量復(fù)雜的處理邏輯(這里寫的是超級簡化版的。。。實際應(yīng)用場景中會復(fù)雜的多)。

class X extends Page {

  state = {
    list: []
  };

  componentDidMount() {
    this._fetchData();
  }

  _fetchData = () => setTimeout(() => this.setState({list: [1,2,3]}), 2000);

  onClickHeader = () => console.log("click header");
  
  onClickBody = () => console.log("click body");
  
  onClickFooter = () => console.log("click footer");

  _renderHeader = () => 
; _renderBody = () => ; _renderFooter = () =>
; render = () => ( {this._renderHeader()} {this._renderBody()} {this._renderFooter()} ); }

在這種情況下,假如產(chǎn)品要對這個頁面做AB該怎么辦呢?為了方便做AB,我們當然希望創(chuàng)建一個新的Page組件,然后在源頭上根據(jù)AB實驗分別跳轉(zhuǎn)到PageA和PageB即可。但是如果真的copy一份PageA作為PageB,再修改其render方法的話,那請你好好保重。。。要不然怎么辦嘞?另一種很容易想到的辦法是在原來Page的render方法中做AB,如下代碼:

class X extends Page {

  // ...省略

  _renderHeaderA = () => ;

  _renderBodyA = () => ;

  _renderFooterA = () => ;

  _renderHeaderB = () => ;

  _renderBodyB = () => ;

  _renderFooterB = () => ;

  render = () => {
    const {version} = this.props;
    return version === 1 ? (
      
        {this._renderHeaderA()}
        {this._renderBodyA()}
        {this._renderFooterA()}
      
    ) : (
      
        {this._renderHeaderB()}
        {this._renderBodyB()}
        {this._renderFooterB()}
      
    );
  }
}

可是這種處理方式有一個很大的弊端!作為Page組件,往往代碼量都會比較大,要是再寫一堆的renderXXX方法那這個文件勢必更加臃腫了。。。要是再改版C、D怎么辦?而且非常容易寫出諸如version === 1 ? this._renderA() : this._renderB()之類的代碼,甚至還有各版本耦合在一起的代碼,到了后期就更加沒法維護了。

那你到底想怎樣。。。為了解決上面臃腫的問題,或許我們可以嘗試把這些render方法給移到另外的文件中(這里需要注意兩點:由于this問題,我們需要將Page的實例作為ctx傳遞下去;為了保證組件能夠正常render,需要把state展開傳遞下去),看下代碼:

說實話,這段代碼寫的足夠惡心。。。好好的一個組件被拆得支離破碎,用到this的地方全部被替換成了ctx,還將整個state展開傳遞下去,看著就很隔應(yīng),而且很不習慣,對于新接手的人來說也容易造成誤解。所以這種hack的方式還是不行,那么到底應(yīng)該怎么辦呢?

噔噔噔噔,高階組件又要出場了~ 在改造這個Page之前,我們先來想下,現(xiàn)在這個例子和剛才的呼吸動畫那個例子有沒有什么相似的地方?答案就是:許多邏輯部分都相同,不同點在于渲染部分。所以,我們的重點在于控制render部分,同時還要解決this的指向問題。來看下代碼:

重點在兩處:一處是constructor的最后一句,我們將renderEntity中方法都綁定到了Page的實例上;另一處則是render方法,我們通過call的方式巧妙地修改了this的指向問題。這樣一來,對于PageA和PageB而言,就完全用不到ctx了。我們再來對比下原來的Page組件,利用高階組件,我們完全就是將相關(guān)的render方法挪了一個位置而已,無形之中還保證了本次修改不會影響到原來的功能。

到了這兒,問題似乎都迎刃而解,但其實還有一個瑕疵。。。啥?到底有完沒完。。。不信,這時候你給PageB中的子組件再加一個onPressXXX事件試試。是哦,這時候事件該加在哪兒呢。。。很簡單,有了renderEntity這個先例,再來一個eventEntity不就好了嗎。。??聪麓a:

真的是不加不知道,一加嚇一跳。。。有了eventEntity之后,思路瞬間豁然開朗。因為通過eventEntity,我們可以將PageA,PageB的事件各自管理,邏輯也被解耦了。我們可以將各版本Page通用的事件仍然保留在Page中,但是各頁面獨有的事件寫在各自的eventEntity中維護。要是日后再想添加新版本的PageC、PageD,或是廢棄PageA,維護管理起來都非常方便。

按照劇情,逼也裝夠了,其實到這里應(yīng)該要結(jié)束了,可是誰讓我又知道了高階組件的反向繼承模式呢。。。前一種的方法唯一的缺點就在于為了hack,我們無形中將PageA和PageB拆的支離破碎,各種方法散落在Object的各個角落。而反向繼承的巧妙之處就在于高階函數(shù)返回的可以是一個繼承自傳進來的組件的組件,因此對于之前的代碼,我們只要稍加改動即可。看下代碼:

相比前一種方法,現(xiàn)在的PageA、PageB顯得更加組件了。所以啊,這繞來繞去的,到頭來卻感覺就只邁出了一小步。。。還記得剛才說要圈起來重點考的那句話嗎?對于這個多版本組件的例子,我們只不過是利用高階組件的形式賦予了PageA,B,C,D這類組件處理該頁面業(yè)務(wù)邏輯的能力。

4. 三點思考 4.1 高階組件有啥好處?

想必通過上面的兩個實際例子,各位看官多多少少已經(jīng)夠體會到高階組件的好處,因為它確實能夠幫助解決平時業(yè)務(wù)開發(fā)中的痛點。其實,高階組件就是把一些通用的處理邏輯封裝在一個高階函數(shù)中,然后返回一個擁有這些邏輯的組件給你。這樣一來,你就賦予了一個普通組件某種能力,同時對該組件的入侵也較小。所以啊,如果你的代碼中充斥著大量重復(fù)性的工作,還不趕緊用起來?

4.2 啥時候用高階組件?

雖然是建議用高階組件來解決問題,但可千萬別啥都往高階組件上套。。。實話實說,我還真見過這樣的代碼。。。但是其實呢,高階組件本身也只是封裝組件的一種方式而已。就比方說文中Loading組件的那個例子,不用高階不照樣能封裝一個組件來簡化重復(fù)性工作嗎?

那究竟什么時候用高階比較合適呢?還記得先前強調(diào)了兩遍的那句話么?“高階組件可以賦予一類組件某種能力” 注意這里的關(guān)鍵詞【一類】,在你準備使用高階組件之前想一想,你接下來要做的事情是不是賦予一類組件某種能力?不妨回想一下上面的兩個例子,第一個例子是賦予了一類普通組件能夠呼吸動畫的能力,第二個例子是賦予一類Page組件能夠處理當前頁面業(yè)務(wù)邏輯的能力。除此之外,還有一個例子也是特別合適,那就是Animated.createAnimatedComponent,它也是賦予了一類普通組件能夠響應(yīng)Animated.Value變化的能力。所以啊,某種程度上你可以把高階組件理解為是一種黑魔法,一旦加上了它,你的組件就能擁有某種能力。這個時候,使用高階組件來封裝你的代碼再合適不過了。

另外,高階組件還有一項非常厲害的優(yōu)勢,那就是可以組合。當然了,本文的例子并沒有體現(xiàn)出這種能力。但是試想,假如你手上有許多個黑魔法(即高階組件),當你把它們自由組合在一起加到某個組件上時,是不是可以創(chuàng)造出無限的可能?而相反,如果你在封裝一個組件的時候集成了全部這些功能,這個組件勢必會非常臃腫,而當另外的組件需要其中某幾個類似的功能時,代碼還不能復(fù)用。。。

4.3 該怎么使用高階組件?

高階組件其實共分為兩種模式:屬性代理 和 反向繼承。分別對應(yīng)上文中的第一個、第二個例子。那該怎么區(qū)分使用呢?嘿嘿,自己用用就知道了??吹脑俣?,不如自己動手寫一個來的理解更深。本文不是高階組件的使用教程,只是兩個用高階組件解決實際問題的例子而已。要真想進一步深入了解高階組件,可以看介紹高階組件的文章,然后動手實踐慢慢體會~ 等到你回過頭來再想一下的時候,必定會有一種豁然開朗的感覺。

5. 寫在最后

都說高階組件大法好,以前都嗤之以鼻,直到抱著試一試的心態(tài)才發(fā)現(xiàn)。。。

真香真香。。。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98175.html

相關(guān)文章

  • 高階組件 + New Context API = ?

    摘要:但是老版的存在一個嚴重的問題子孫組件可能不更新。其中,某一時刻屬性發(fā)生變化導(dǎo)致組件觸發(fā)了一次渲染,但是由于組件是且并未用到屬性,所以的變化不會觸發(fā)及其子孫組件的更新,導(dǎo)致組件未能得到及時的更新。 1. 前言 繼上次小試牛刀嘗到高價組件的甜頭之后,現(xiàn)已深陷其中無法自拔。。。那么這次又會帶來什么呢?今天,我們就來看看【高階組件】和【New Context API】能擦出什么火花! 2. N...

    Joyven 評論0 收藏0
  • 動手寫個React高階組件

    摘要:作用是給組件增減屬性。如果你的高階組件不需要帶參數(shù),這樣寫也是很的。那么需要建立一個引用可以對被裝飾的組件做羞羞的事情了,注意在多個高階組件裝飾同一個組件的情況下,此法并不奏效。你拿到的是上一個高階組件的函數(shù)中臨時生成的組件。 是什么 簡稱HOC,全稱 High Order Component。作用是給react組件增減props屬性。 怎么用 為什么不先說怎么寫?恩,因為你其實已經(jīng)用...

    xiaokai 評論0 收藏0
  • React中的函數(shù)子組件(FaCC)和高階組件(HOC)

    摘要:高階函數(shù)我們都用過,就是接受一個函數(shù)然后返回一個經(jīng)過封裝的函數(shù)而高階組件就是高階函數(shù)的概念應(yīng)用到高階組件上使用接受一個組件返回一個經(jīng)過包裝的新組件。靈活性在組合階段相對更為靈活,他并不規(guī)定被增強組件如何使用它傳遞下去的屬性。 在接觸過React項目后,大多數(shù)人都應(yīng)該已經(jīng)了解過或則用過了HOC(High-Order-Components)和FaCC(Functions as Child ...

    elliott_hu 評論0 收藏0
  • 寫給自己的React HOC(高階組件)手冊

    前言 HOC(高階組件)是React中的一種組織代碼的手段,而不是一個API. 這種設(shè)計模式可以復(fù)用在React組件中的代碼與邏輯,因為一般來講React組件比較容易復(fù)用渲染函數(shù), 也就是主要負責HTML的輸出. 高階組件實際上是經(jīng)過一個包裝函數(shù)返回的組件,這類函數(shù)接收React組件處理傳入的組件,然后返回一個新的組件. 注意:前提是建立在不修改原有組件的基礎(chǔ)上. 文字描述太模糊,借助于官方...

    W4n9Hu1 評論0 收藏0
  • Render props、render callback 和高階組件皆可互換

    摘要:現(xiàn)在來看看怎么使用高階組件來達到同樣的目的。在這個新的組件里包含了加強的和等內(nèi)容。有時會遇到一個提供了的庫,但是你喜歡的是高階組件。我們來根據(jù)上面的例子來加一些方法可以讓高階組件和模式可以互相轉(zhuǎn)換。總結(jié),回調(diào)繪制和高階組件都是可以互換的。 讓 render-xxx 模式都可以互換。 基礎(chǔ) 所有上面提到的三種模式都是為了處理 mixin 要處理的問題的。在我們繼續(xù)之前,我們來看一些例子。...

    姘擱『 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<