[譯] 理解 React Render Props 和 HOC

wuzhengyan2015發表於2018-12-23

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

[譯] 理解 React Render Props 和 HOC

reactjs.org

如果你最近有在做 React 開發,你肯定有遇到像 HOCs 和 Render Props 這樣的術語。在本文中,我們將深入探討這兩種模式,以便了解我們為什麼需要它們,以及如何正確地使用它們來構建更好的 React 應用。

為什麼我們需要這些模式?

React 提供了一種簡單的程式碼複用方式,即元件。元件封裝了很多東西,包括內容、樣式和業務邏輯。理想情況下,在單個元件中,我們可以將 html、css 和 js 結合起來,所有的這些是為了一個目的,單一職責

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

例子

假設我們正在開發一個電子商務應用程式。它與其他電子商務應用程式一樣,向使用者顯示所有可購買產品,並且使用者可以將任何產品新增到購物車。我們將從 API 獲取產品資料,並將產品目錄顯示為卡片列表。

在這種情況下,React 元件可以像這樣實現:

[譯] 理解 React Render Props 和 HOC

程式碼連結

對於我們的管理員,有一個管理門戶,他們可以在其中新增或刪除產品。在此門戶中,我們從同一 API 獲取產品資料,並以表格形式顯示產品目錄。

這個 React 元件可以像這樣實現:

[譯] 理解 React Render Props 和 HOC

程式碼連結

有一件事很明顯,這兩個元件都實現了產品的資料獲取邏輯。

繼續深入,以下這些情況也可能出現:

  • 我們必須使用產品資料並以不同的方式顯示它。
  • 從不同的 API 中獲取產品資料(在使用者的購物車頁面中很有用),但資料的顯示和我們在 ProductList 中的做法類似。
  • 我們必須從 localStorage 訪問它,而不是從 API 獲取資料。
  • 在產品目錄表格中,需要使用具有不同操作的按鈕而不是刪除按鈕。

如果我們為這些每一點都寫個不同的元件,那麼我們將要複製大量的程式碼。

獲取資料和顯示資料是兩個獨立的關注點。正如前面所說的,如果一個元件有一個責任會更好。

讓我們重構第一個元件。它將接受產品資料為屬性,並像之前一樣把產品目錄渲染成卡片列表。由於我們不需要元件狀態和生命週期方法,我們把它轉換成函式式元件。

它現在看起來是這樣的:

[譯] 理解 React Render Props 和 HOC

ProductList.js (程式碼連結)

就像 ProductListProductTable 會是一個函式元件,它接收產品資料為屬性,並把資料渲染到表的行中去。

現在讓我們建立一個名為 ProductsData 的元件。它從 API 獲取產品資料。資料的獲取和狀態的更新將和原先的 ProductList 元件一樣。但是我們應該在這個元件的 render 方法中放入什麼呢?

[譯] 理解 React Render Props 和 HOC

ProductData.js (程式碼連結)

如果我們只是簡單的放入 ProductList 元件,那麼我們就不能複用這個元件於 ProductTable。不管怎樣,如果這個元件可以詢問要渲染什麼,那個問題就會得到解決。在一個地方,我們將告訴它要渲染 ProductList 元件,而在管理門戶中,我們告訴它要渲染 ProductTable 元件。

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

現在我們知道了為什麼需要它們,讓我們來看看如何使用它們。

Render Props

在概念層面理解 Render Props 非常簡單。讓我們忘掉 React 一會,然後看看原生 JavaScript 下的事情。

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

[譯] 理解 React Render Props 和 HOC

但是,我們很快發現 sum 函式非常有用,我們需要在其他地方使用到它。因此,我們希望 sum 函式只提供結果,而不是將其記錄到控制檯,並讓呼叫者決定如何使用結果。

它可以這麼做:

[譯] 理解 React Render Props 和 HOC

程式碼連結

我們傳給 sum 函式一個 fn 回撥函式作為引數。然後 sum 函式計算結果並把結果作為引數呼叫 fn。通過這種方式,回撥函式可以獲得結果,並且可以自由地對結果進行任何操作。

這就是 Render Props 的本質。我們將通過使用這個模式來更清晰地認識它,所以讓我們立刻把它用到我們現在面臨的問題中去吧。

在這不是計算兩個數字之和的函式,而是獲取產品資料的元件 ProductsData。現在可以通過屬性傳遞給 ProductsData 元件一個函式。然後 ProductsData 元件將獲取產品資料,並將這些資料提供給以屬性方式傳遞進來的函式。傳遞進來的函式現在可以對產品資料做任何它想做的事情。

在 React 中,它可以像這樣實現:

[譯] 理解 React Render Props 和 HOC

程式碼連結

就像 fn 引數,我們有一個 render 屬性,它將作為一個函式被傳遞。然後 ProductData 元件把產品資料作為引數呼叫這個函式。

因此我們可以以這種方式使用 ProductData 元件。

[譯] 理解 React Render Props 和 HOC

程式碼連結

正如我們所看到的 Render Props 是一種相當通用的模式。大部分事情都可以非常直接地完成。但這也是我們搬起石頭砸自己的腳的原因:

[譯] 理解 React Render Props 和 HOC

[譯] 理解 React Render Props 和 HOC

[譯] 理解 React Render Props 和 HOC

避免巢狀的一種簡單方法是把元件拆解成更小的元件,並將這些元件儲存在單獨的檔案中。另一種方法是編寫更多的元件並組合它們,而不是在 Render Props 中使用長函式。

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

高階元件(HOC)

在這個模式中,我們定義了一個函式,該函式接受一個元件作為引數,然後返回相同的元件,但是新增了一些功能。

如果這聽起來很熟悉,那是因為它類似於 Mobx 中廣泛使用的裝飾器模式。像 Python 這樣的許多語言都內建了裝飾器,JavaScript也很快就會支援裝飾器。HOCs 很像裝飾器。

比起用文字解釋,用通過程式碼來理解 HOCs 會容易很多。所以讓我們先來看程式碼。

[譯] 理解 React Render Props 和 HOC

程式碼連結

正如我們所看到的,資料獲取和狀態更新邏輯就和我們在 Render Props 所做的一樣。唯一的變化就是元件類是位於函式內部。該函式接受一個元件為引數,然後在內部的 render 方法中渲染這個元件,但是會新增額外的屬性。對於名稱如此複雜的模式,實現起來相當簡單,對吧?

[譯] 理解 React Render Props 和 HOC

程式碼連結

現在我們已經瞭解了為什麼我們需要 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前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章