[譯] 理解 React Render Props 和 HOC

React 中 Render Props 和高階組件的詳細介紹

reactjs.orgjavascript

若是你最近有在作 React 開發,你確定有遇到像 HOCs 和 Render Props 這樣的術語。在本文中,咱們將深刻探討這兩種模式,以便了解咱們爲何須要它們,以及如何正確地使用它們來構建更好的 React 應用。css

爲何咱們須要這些模式?

React 提供了一種簡單的代碼複用方式,即組件。組件封裝了不少東西,包括內容、樣式和業務邏輯。理想狀況下,在單個組件中,咱們能夠將 html、css 和 js 結合起來,全部的這些是爲了一個目的,單一職責html

提示:使用 Bit (Github),你能夠組織和分享可複用的組件,這些組件能夠從不一樣的項目和應用中被發現,分享和開發。這比重寫組件或者維護一個大型庫要快得多。試試看 :)前端

例子

假設咱們正在開發一個電子商務應用程序。它與其餘電子商務應用程序同樣,向用戶顯示全部可購買產品,而且用戶能夠將任何產品添加到購物車。咱們將從 API 獲取產品數據,並將產品目錄顯示爲卡片列表。java

在這種狀況下,React 組件能夠像這樣實現:react

代碼連接android

對於咱們的管理員,有一個管理門戶,他們能夠在其中添加或刪除產品。在此門戶中,咱們從同一 API 獲取產品數據,並以表格形式顯示產品目錄。ios

這個 React 組件能夠像這樣實現:git

代碼連接github

有一件事很明顯,這兩個組件都實現了產品的數據獲取邏輯。

繼續深刻,如下這些狀況也可能出現:

  • 咱們必須使用產品數據並以不一樣的方式顯示它。
  • 從不一樣的 API 中獲取產品數據(在用戶的購物車頁面中頗有用),但數據的顯示和咱們在 ProductList 中的作法相似。
  • 咱們必須從 localStorage 訪問它,而不是從 API 獲取數據。
  • 在產品目錄表格中,須要使用具備不一樣操做的按鈕而不是刪除按鈕。

若是咱們爲這些每一點都寫個不一樣的組件,那麼咱們將要複製大量的代碼。

獲取數據和顯示數據是兩個獨立的關注點。正如前面所說的,若是一個組件有一個責任會更好。

讓咱們重構第一個組件。它將接受產品數據爲屬性,並像以前同樣把產品目錄渲染成卡片列表。因爲咱們不須要組件狀態和生命週期方法,咱們把它轉換成函數式組件。

它如今看起來是這樣的:

ProductList.js (代碼連接)

就像 ProductListProductTable 會是一個函數組件,它接收產品數據爲屬性,並把數據渲染到表的行中去。

如今讓咱們建立一個名爲 ProductsData 的組件。它從 API 獲取產品數據。數據的獲取和狀態的更新將和原先的 ProductList 組件同樣。可是咱們應該在這個組件的 render 方法中放入什麼呢?

ProductData.js (代碼連接)

若是咱們只是簡單的放入 ProductList 組件,那麼咱們就不能複用這個組件於 ProductTable。無論怎樣,若是這個組件能夠詢問要渲染什麼,那個問題就會獲得解決。在一個地方,咱們將告訴它要渲染 ProductList 組件,而在管理門戶中,咱們告訴它要渲染 ProductTable 組件。

這就是 Render Props 和 HOCs 發揮做用的地方。它們只是一類方法,即對於一個組件,會詢問應該渲染什麼內容。這進一步推進了代碼的複用。

如今咱們知道了爲何須要它們,讓咱們來看看如何使用它們。

Render Props

在概念層面理解 Render Props 很是簡單。讓咱們忘掉 React 一會,而後看看原生 JavaScript 下的事情。

咱們有一個計算兩個數字之和的函數。起初咱們只想要把結果記錄到控制檯。因此,咱們這樣設計函數:

可是,咱們很快發現 sum 函數很是有用,咱們須要在其餘地方使用到它。所以,咱們但願 sum 函數只提供結果,而不是將其記錄到控制檯,並讓調用者決定如何使用結果。

它能夠這麼作:

代碼連接

咱們傳給 sum 函數一個 fn 回調函數做爲參數。而後 sum 函數計算結果並把結果做爲參數調用 fn。經過這種方式,回調函數能夠得到結果,而且能夠自由地對結果進行任何操做。

這就是 Render Props 的本質。咱們將經過使用這個模式來更清晰地認識它,因此讓咱們馬上把它用到咱們如今面臨的問題中去吧。

在這不是計算兩個數字之和的函數,而是獲取產品數據的組件 ProductsData。如今能夠經過屬性傳遞給 ProductsData 組件一個函數。而後 ProductsData 組件將獲取產品數據,並將這些數據提供給以屬性方式傳遞進來的函數。傳遞進來的函數如今能夠對產品數據作任何它想作的事情。

在 React 中,它能夠像這樣實現:

代碼連接

就像 fn 參數,咱們有一個 render 屬性,它將做爲一個函數被傳遞。而後 ProductData 組件把產品數據做爲參數調用這個函數。

所以咱們能夠以這種方式使用 ProductData 組件。

代碼連接

正如咱們所看到的 Render Props 是一種至關通用的模式。大部分事情均可以很是直接地完成。但這也是咱們搬起石頭砸本身的腳的緣由:

避免嵌套的一種簡單方法是把組件拆解成更小的組件,並將這些組件保存在單獨的文件中。另外一種方法是編寫更多的組件並組合它們,而不是在 Render Props 中使用長函數。

接下來,咱們將看下另外一種流行的模式,它被稱爲 HOC。

高階組件(HOC)

在這個模式中,咱們定義了一個函數,該函數接受一個組件做爲參數,而後返回相同的組件,可是添加了一些功能。

若是這聽起來很熟悉,那是由於它相似於 Mobx 中普遍使用的裝飾器模式。像 Python 這樣的許多語言都內置了裝飾器,JavaScript也很快就會支持裝飾器。HOCs 很像裝飾器。

比起用文字解釋,用經過代碼來理解 HOCs 會容易不少。因此讓咱們先來看代碼。

代碼連接

正如咱們所看到的,數據獲取和狀態更新邏輯就和咱們在 Render Props 所作的同樣。惟一的變化就是組件類是位於函數內部。該函數接受一個組件爲參數,而後在內部的 render 方法中渲染這個組件,可是會添加額外的屬性。對於名稱如此複雜的模式,實現起來至關簡單,對吧?

代碼連接

如今咱們已經瞭解了爲何咱們須要 Render Props,HOCs 以及咱們如何實現它們。

還有一個問題:如何在 Render Props 和 HOCs 中進行選擇?關於這個話題的文章已經有不少了,因此我如今不討論這個話題。也許在個人下一篇文章中 :)

何時不要使用 Render Props — Kent C. Dodds

HOCs vs Render Props — Richard Kotze

結論

在本文中,咱們瞭解了爲何須要這些模式,每一個模式的本質和如何利用這些模式來構建高度可複用的組件。以上就是所有內容,但願你喜歡,請隨意評論和提問。我很樂意交流 👏

2018 年 10 月更新:React hooks 已經在 alpha 版本中中發佈。它們將消除編寫類組件、HOCs 和 Render Props 的痛苦。我很快就會寫一篇來解釋,關注個人 TwitterMedium 或者訂閱 個人時事通信 來獲取最新消息。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索