高階組件是什麼呢?高階組件(HOC)是React中用於重用組件邏輯的高級技術。HOC本身不是React API的一部分。它們是從React的組成性質中產生的模式。高階組件就是一個函數,傳給它一個組件,它返回一個新的組件。

在使用React構建項目的過程中,經常會碰到這樣的情況,在不同的組件中需要用到相同功能。當我們還是一隻小白的時候,我們的解決方法是,去這些組件中到處粗暴編寫同樣的代碼。

下面開始我們的實例:

我們想要的結果是醬嬸的

簡單粗暴的實現過程:

渲染結果

通過上圖我們可以看出,雖然同樣可以實現功能,但是這樣寫代碼並不優雅。我們需要優化一下,此刻,高級組件就要登場了

實現高階組件的方法有兩種。

屬性代理(props proxy)。高階組件通過被包裹的React組件來操作props。反向繼承(inheritance inversion)高階組件繼承於被包裹的react組件
  1. 屬性代理

下面我們就寫一個高階組件,編寫一個名myContainer的高階組件

從這裡我們可以看到重要的部分是render方法中返回了傳入的WrappedComponent的react組件,這樣,我們就可以通過高階組件來傳遞props,這種方法即為屬性代理

使用方法:

隨後,觀察React組件樹發生了什麼變化,如圖所示,可以發現Welcome組件被NewComponent組件包裹起來了,即組件是層層包裹起來的,如同洋蔥一樣。

這樣的組件,就可以一層層的作為參數被調用,原始組件就具備了高階組件對它的修飾,保持單個組件封裝性的同時還保留了易用性。就這麼簡單啦~

通過上面的栗子可以看到,高階組件和父組件很相似,但是高階組件是一個函數,函數關注的是邏輯;父組件是一個組件,組件主要關注的是UI/DOM。如果邏輯是與DOM直接相關的,那麼這部分邏輯適合放到父組件中實現;如果邏輯是與DOM不直接相關的,那麼這部分邏輯適合使用高階組件抽象。

此處插播一條廣告,NewComponent這個高階組件可能用到不止一次呢,如果很多地方都用到的話,對於技術上沒有什麼問題,但是對於我們開發調試來說,可就有點費勁了。此時能有個標識就再好不過了。看我們如何實現心中所想

在高階組件中寫入這兩行代碼,每個高階組件都會有自己的名字,這樣對於我們調試來說,簡直不要太貼心。展示結果如下圖??所示

從功能上,高階組件一樣可以對組件進行控制。包括控制props、通過refs使用,引用、抽象state,下面我們就對這些功能一一進行解釋

控制props

我們可以讀取,增加,編輯或者是移除從WrappedComponent傳進來的props

然後我們的Welcome組件render的時候, console.log(this.props) 會得到handleClick。如下圖所示

通過refs使用引用

在高階組件中,我們可以接受refs使用WrappedComponent的引用

輸出的結果是:

當wrappedComponent被渲染時,refs回調函數就會被執行,這樣我們就可以方便的讀取和增加實例的props,並調用實例的方法。是不是很神奇呢

抽象state

知道了如何用refs來獲取組件實例,我們看一下如何抽離state。我們可以通過WrappedComponent提供的props和回調函數抽象state。這種情況一般是react處理表單的時候用的比較多一些。請看下圖

我們寫個react提交的表單頁,一般情況下,react在處理表單提交的時候,一般使用的是受控組件,即把input都做成受控的,改變value的時候,用onChange事件同步到state中。

在myContainer中的處理:

如上圖所示,可以看到,我們在這裡把state,onChange等方法都放到myContainer這個高階組件中。

在Welcome組件中的處理

這樣處理的優勢:如果state,處理函數放到Welcome組件里,這樣只會讓Welcome更加笨重,承擔了本不屬於它的工作。而利用高階組件的話,高階組件獲取到用戶名密碼之後,再去做其他操作,這樣就方便多了,而且遵從了react組件的一種規範,子組件簡單,只負責展示,邏輯與操作放到高階組件中。

2. 反向繼承

另一種構建高階組件的方法稱為反向繼承。但從字面意思,就可以看到和繼承有一定的關係,看一個簡單的實現:

高階組件myContainer:

Welcome組件

輸出結果:

正如結果所示,高階組件返回的組件繼承於WrappedComponent,由於被動的繼承了WrappedComponent,所有的方法都會反向,所以叫做反向繼承。

在反向繼承的方法中,高階組件可以使用WrappedComponent的state,props, 生命周期和render方法。反向繼承的主要特點是渲染劫持

渲染劫持

劫持,劫持的字面意思就是我可以控制你,並且控制你做各種各樣的事情。渲染劫持就是高階組件可以控制WrappedComponent的渲染過程。

我們可以在這個過程中在任何React元素輸出的結果中讀取、增加、修改、刪除props,或讀取或修改React元素樹,或條件顯示元素樹,又或是用樣式控制包裹元素樹

栗子時間

渲染劫持之前,頁面渲染結果

組件渲染:

myContainer高階組件渲染劫持實現

在上面??的栗子中,可以看到WrappedComponent的渲染結果中,頂層的input組件的value被改寫為 welcome home。

頁面渲染和組件的渲染的結果沒有什麼區別,但是在後者的WrappedComponent中,多了一個判斷條件,我們可以控制WrappedComponent的元素樹,並輸出相應的結果。因此,我們可以做各種各樣的事,甚至可以反轉元素樹,或者改造樹中的props。

到這裡,高階組件的介紹差不多已經接近尾聲了。但是請注意,HOC不會修改輸入組件,也不會使用繼承來複制其行為。相反,HOC 通過將原始組件包裝在容器組件中來組成原始組件。HOC是純函數,沒有副作用。

高階組件是Decorator模式在React的一種實現,它可以抽離公共邏輯,像洋蔥一樣層層疊加給組件,每一層職能分明,可以方便地抽離與增添。在優化代碼或解耦組件時,可以考慮使用高階組件模式。

高階組件的使用:

React-Redux:

React-Redux 是 Redux 官方的 React 綁定實現。它提供的函數中有一個 connect,處理了監聽 store 和後續的處理。是通過 Props Proxy 來實現的。

Radium:

Radium是一組用於管理React元素上的內聯樣式的工具。它為我們提供強大的樣式功能,無需CSS。

Radium 是怎樣做到內聯 CSS 偽類的?Radium 需要讀取 WrappedComponent 的 render 方法輸出的所有組件樹,每當它發現一個新的帶有 style 屬性的組件時,在 props 上添加一個事件監聽器。簡單地說,Radium 修改了組件樹的 props

推薦閱讀:

相关文章