React高階組件-總結

React高階組件

索引

在React組件的構建過程當中,經常有這樣的場景,有一類功能須要被不一樣的組件公用,此時,就涉及抽象的話題,在不一樣設計理念下,有許多的抽象方法,而針對React,咱們重點討論兩種:mixin和高階組件。

Mixin

mixin的特性一直普遍存在於各類面嚮對象語言中。好比在Ruby中,include關鍵詞便是mixin。是將一個模塊混入到一個另外一個模塊中,或是一個類中。爲何編程語言要引入這樣一種特性呢?事實上,包括C++等一些年齡較大的OOP語言,它們都有一個強大但危險的多重繼承特性。在現代語言中,爲了權衡利弊,大都捨棄了多重繼承,只採用單繼承,但單繼承在實現抽象時有不少不方便的地方,爲了彌補缺失,java引入了接口interface。其餘一些語言則引入了像mixin的技巧。javascript

alt text

Mixin的問題

從上面的代碼,咱們不難看出,對於廣義的mixin方法,就是用賦值的方式將mixin對象裏的方法都掛載到原對象上,來實現對對象的混入。java

從上述的實現,咱們能夠聯想到 underscore庫中的extend 或 lodash庫中的 assign方法,或者說ES6中的Object.assign()方法。react

React官方已不推薦使用Mixins的技術來實現代碼的重用,Mixins技術有一系列的缺點,首先Mixins會形成命名衝突,若是你須要注入多個mixins,其中一個是本身的,另外的多是第三方的。那有可能在兩個mixins裏使用了相同名稱的方法,這會使得其中的一個不起做用,而你能作的只有修改其中一個方法的名稱。另外一方面,一個mixins一開始多是很是簡單的,僅僅須要實現某一個功能,但當業務越加的複雜,須要往其中加入更多的方法的時候,就會變得很是複雜。針對這些困擾,React社區提出來新的方式來取代mixin,那就是高階組件。編程

概念

說到高階組件,就先得說到高階函數了,高階函數是至少知足下列條件的函數在javascript這門函數爲一等公民的語言中,高階函數的使用仍是很是之多的,像咱們平時的回調函數等等,都用到了高階函數的知識。咱們先來看一個簡單的高階函數redux

alt text

從上面的代碼,咱們不難看出,對於廣義的mixin方法,就是用賦值的方式將mixin對象裏的方法都掛載到原對象上,來實現對對象的混入。從上述的實現,咱們能夠聯想到 underscore庫中的extend 或 lodash庫中的 assign方法,或者說ES6中的Object.assign()方法。設計模式

高階組件實際上是差很少的用法,類比高階函數的定義,高階組件就是接受一個組件做爲參數,在函數中對組件作一系列的處理,隨後返回一個新的組件做爲返回值。高階組件(HOC)是 React 中用於重用組件邏輯的高級技術。 HOC 自己不是 React API 的一部分。 它們是從 React 構思本質中浮現出來的一種模式,這種模式是由React自身的組合性質必然產生的。具體來講,高階組件是一個函數,可以接受一個組件做爲參數,在函數中對組件作一系列的處理,隨後返回一個新的組件做爲返回值。在咱們項目中使用react-redux框架的時候,有一個connect的概念,這裏的connect其實就是一個高階組件。也包括相似react-router-dom中的withRouter的概念。babel

裝飾器模式

先來一個最簡單的demo

alt text

alt text

組件Usual經過simpleHoc的包裝,打了一個log... 那麼形如simpleHoc就是一個高階組件了,經過接收一個組件class Usual,並返回一個組件class。 其實咱們能夠看到,在這個函數裏,咱們能夠作不少操做。 並且return的組件一樣有本身的生命週期,function,另外,咱們看到也能夠把props傳給WrappedComponent(被包裝的組件)。markdown

高階組件能夠看作是裝飾器模式(Decorator Pattern)在React的實現。即容許向一個現有的對象添加新的功能,同時又不改變其結構,屬於包裝模式(Wrapper Pattern)的一種ES7中添加了一個decorator的屬性,使用@符表示,能夠更精簡的書寫。那上面的例子就能夠改爲 是一樣的效果。固然兼容性是存在問題的,一般都是經過babel去編譯的。 babel提供了plugin,transform-decorators-legacy。react-router

alt text

實現方式

實現高階組件的方法有以下兩種:屬性代理、反向繼承。app

屬性代理:高階組件經過被包裹的React組件來操做props

屬性代理有以下4點常見做用:

  1. 操做props

  2. 經過refs訪問組件實例

  3. 提取state

  4. 用其餘元素包裹WrappedComponent,實現佈局等目的

alt text

alt text

alt text

alt text

能夠經過傳入 props 和回調函數把 state 提取出來使用ppHOC裝飾器以後,組件的props被添加了name屬性,能夠經過下面的方法,將value和onChange添加到input上面 input會成爲受控組件

反向繼承:高階組件繼承於被包裹的React組件。

反向繼承能夠劫持被繼承class的render內容,進行修改,過濾後,返回新的顯示內容。 之因此被稱爲渲染劫持是由於 HOC 控制着 WrappedComponent 的渲染輸出,能夠用它作各類各樣的事。

alt text

alt text

如上代碼。高階組件返回的組件繼承於 WrappedComponent 。由於被動地繼承了 WrappedComponent,全部的調用都會反向,這也是種方法的由來。這種方法與屬性代理不太同樣。它經過繼承WrappedComponent來實現,方法能夠經過super來順序調用。由於依賴於繼承機制。HOC的調用順序和隊列是同樣的。在由 render輸出的任何 React 元素中讀取、添加、編輯、刪除 props 讀取和修改由 render 輸出的 React 元素樹 有條件地渲染元素樹 把樣式包裹進元素樹,就行Props Proxy那樣包裹其餘的元素

應用場景

一、兩個頁面UI幾乎同樣,功能幾乎相同,僅僅幾個操做不太同樣,卻寫了兩個耦合不少的頁面級組件

二、以前寫過一個組件A,作完上線,以後加了一個新需求,很奇怪要作的組件B跟A幾乎如出一轍,但稍微有區別

三、Container解決不了的時候甚至不太優雅的時候

四、實現一個從loaclstorage返回記錄的功能

五、Mobx-react、react-redux

A).Container解決不了的時候甚至不太優雅的時候。其實大部分時候包一層Container組件也能作到差很少的效果,好比操做props,渲染劫持。但其實仍是有很大區別的。好比咱們如今有兩個功能的container,添加樣式和添加處理函數的,對Usual進行包裝.

B).mobx-react就是高階組件是一個實際應用@observer裝飾器將組件包裝爲高階組件,傳入組件MyComponent後,mobx-react會對其生命週期進行各類處理,並經過調用forceUpdate來進行刷新實現最小粒度的渲染。mobx提倡一份數據引用,而redux中則提倡immutable思想,每次返回新對象。

C).react-redux中的connect

D).connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])(WrappedComponent)

例如,咱們把組件ComponentA鏈接到Redux上的寫法相似於:

const ConnectedComponentA = connect(mapStateToProps,mapDispatchToProps)(ComponentA);

咱們能夠把它拆分來看:

// connect 是一個函數,返回值enhance也是一個函數

const enhance = connect(mapStateToProps, mapDispatchToProps);

// 返回的函數就是一個高階組件,該高階組件返回一個與Redux store關聯起來的新組件

const ConnectedComponentA = enhance(ComponentA);

connect這個函數會將一個React組件鏈接到Redux 的 store

這個函數會將一個React組件鏈接到Redux 的 store。在鏈接的過程當中,connect經過參數 mapStateToProps,從全局store中取出當前組件須要的state,並把state轉化成當前組件的props;同時經過參數 mapDispatchToProps,把當前組件用到的Redux的action creators,以props的方式傳遞給當前組件。

總結

一、高階組件不會修改子組件,也不拷貝子組件的行爲,建議使用組合。

二、要給hoc添加class名,便於debugger。

三、靜態方法要複製。

四、refs不會傳遞。

五、不要在render方法內部使用高階組件

高階組件是react應用中很重要的一部分,最大的特色就是重用組件邏輯,提升組件的解耦和靈活性,它並非由React API定義出來的功能,而是由React的組合特性衍生出來的一種設計模式。若是你用過redux,那你就必定接觸太高階組件,由於react-redux中的connect就是一個高階組件。

相關文章
相關標籤/搜索