高階函數(shù)一點(diǎn)通


理解快速變化的 React 最佳實(shí)踐

image.png

如果你剛開始接觸 React,你可能已經(jīng)聽說過 “高階組件” 和 “容器” 組件。你也許會奇怪這都什么鬼東西?;蛘吣阋呀?jīng)開始使用庫提供的 API 了,但對于這些個術(shù)語還有些疑惑。

作為 Apollo 的 React 集成 - 一個重度使用高階組件的熱門開源庫 - 的維護(hù)者和文檔作者,我花了些時間來理清這些概念。

我希望這篇文章能夠幫你對這一主題有更進(jìn)一步的了解。

重識 React

本文假定你已對 React 有一定的了解 - 如果沒有的話有很多資料可供查閱。例如 Sacha Greif 的 React 5 大概念 就是很好的入門文章。但是,讓我們再回顧一下然后繼續(xù)我們的文章。

一個 React 應(yīng)用包含一系列 組件。組件中會傳遞一組輸入屬性(props),并且輸出屏幕渲染的 HTML 片段。當(dāng)一個組件的 props 更新時,會觸發(fā)組件重繪,HTML 也會相應(yīng)變化。

當(dāng)用戶通過一種事件(例如鼠標(biāo)點(diǎn)擊)與 HTML 進(jìn)行交互時,組件處理事件要么通過觸發(fā) 回調(diào) prop,要么通過更新內(nèi)部 state。更新內(nèi)部 state 也會造成組件自身及其子組件的重繪。

這里就不得不提組件 生命周期,即組件首次渲染,綁定 DOM,傳遞新 props 等。

組件的渲染函數(shù)返回一個或多個其他組件的實(shí)例。合成 視圖樹 是一個好的思維模型,能夠表明應(yīng)用內(nèi)的組件是如何交互的。通常,組件交互是通過傳遞 props 給子組件實(shí)現(xiàn)的,或者通過觸發(fā)父組件傳遞來的回調(diào)函數(shù)實(shí)現(xiàn)。

image.png

React 視圖樹中的數(shù)據(jù)流

React UI vs 無狀態(tài)

似乎現(xiàn)在已經(jīng)過時,但曾經(jīng)一切都區(qū)分為 Model,View 和 Controller(或者 View Model,或者 Presenter)來描述。在這種分類方式,View 的任務(wù)就是 渲染 并且處理用戶交互,Controller 的任務(wù)則是 準(zhǔn)備數(shù)據(jù)。

React 最近的趨勢是實(shí)現(xiàn) 無狀態(tài)函數(shù)組件。這些簡單的“純”組件只根據(jù)自身的 props 轉(zhuǎn)換成 HTML 和調(diào)用回調(diào) props 來響應(yīng)用戶交互:

image.png

他們是函數(shù)式的,你甚至可以就把他們當(dāng)做函數(shù)。如果你的視圖樹包含“純”組件,你可以把整棵樹看成一個由許多小函數(shù)組成的輸出 HTML 的大型函數(shù)。

無狀態(tài)函數(shù)式組件有個很好的特點(diǎn)是極容易測試,并且易于理解。即易于開發(fā)和快速 debug。

但是你不能一直逃避的是,UI 需要狀態(tài)。比如,當(dāng)用戶滑過菜單時,要自動打開(我希望是不要啦?。? 在 React 是利用 state 來實(shí)現(xiàn)的。要用 state,你就要用基于 class 的組件。

把 UI 的 “全局 state” 引入視圖樹就是事情復(fù)雜的開始。

全局 State

UI 的 全局 state 不能直接獨(dú)立和某個獨(dú)立組件相聯(lián)系。典型地,這一般包含了兩類事情:

  1. 應(yīng)用的 數(shù)據(jù) 從 server 來。通常,數(shù)據(jù)用于多處,所以并不唯一關(guān)聯(lián)某個組件。

  2. 全局 UI state,(像 URL,決定了用戶瀏覽的頁面路徑)。

安置全局 state 的一個方法是應(yīng)用內(nèi)綁定最高層的 “根” 組件,并且下發(fā)到各個需要它的子組件中去。然后 state 的改變再通過一連串的回調(diào)反饋到頂層。

image.png

單容器從 store 到視圖樹的數(shù)據(jù)流。

這一方法即使快但很笨拙。根組件需要理解全樹的需求,每個子樹的父組件同樣需要理解每個子樹的需求。此時引入另一個概念。

容器和展示類組件

這個問題通常通過允許任何層級組件都能獲取全局 state 的方式來解決(要求有一些限制)。

在 React 的世界里,組件可以分為能拿到全局 state 的和不能拿到的。

“純”組件易于測試和理解(尤其是無狀態(tài)函數(shù)式組件)。一旦一個組件是“不純”的,它就被污染了,并且很難處理。

因此,出現(xiàn)了一個 pattern 把“不純”的組件拆分成 兩個 組件:

  • 容器 組件操作“臟”全局 state
  • 展示 組件相反

我們只要像對待上面的一般組件一樣對待展示類組件,但把臟的和復(fù)雜數(shù)據(jù)操作類的工作獨(dú)立到容器組件里。

image.png

多容器的數(shù)據(jù)流

容器

一旦你開始區(qū)分展示類/容器類組件,編寫容器組件會變得有趣。

有件事要注意的是容器類組件有時候不像個組件。它們可能:

  • 獲取并傳遞一個全局 state(可以是 Redux)片段到子組件。
  • 運(yùn)行一個數(shù)據(jù)訪問(可以是 GraphQL)請求,然后把結(jié)果傳給子組件。

當(dāng)然,如果我們遵循好的拆分原則,容器 只掛載單個子組件。容器和子組件強(qiáng)綁定,因?yàn)樽咏M件天生在 render 方法里。不是么?

容器歸納

對于容器組件的 眾多類型 來說(例如,某個容器組件訪問的是 Redux store),實(shí)現(xiàn)基本相同,不同在于細(xì)節(jié):渲染的子組件的不同,獲取數(shù)據(jù)的不同。

舉個栗子,在 Redux 的世界里,容器可能是這樣的:

image.png

雖然這個容器很多功能不像真的 Redux 容器,你可以看到除了 mapStateToProps 的實(shí)現(xiàn)和我們包裝的特定 MyComponent,每次寫訪問 Redux 的容器,我們還要寫很多模板代碼。

生成容器

事實(shí)上,寫一個自動 生成 容器組件的方法會更容易,這個方法基于相關(guān)信息(此例中是子組件和 mapStateToProps 函數(shù))。

image.png

這是一個 高階組件(HOC),是以子組件和其他選項作為參數(shù),為該子組件構(gòu)造容器的函數(shù)。

“高階”即“高階函數(shù)” - 構(gòu)造函數(shù)的函數(shù),事實(shí)上,可以認(rèn)為 React 組件是產(chǎn)出 UI 的組件。尤其在無狀態(tài)函數(shù)式組件中,這一方法尤其實(shí)用,但是仔細(xì)想想,它在純狀態(tài)展示組件中也同樣實(shí)用。HOC 其實(shí)就是高階函數(shù)。

HOC 例子

這里有些值得一看的例子:

  • 最普遍的可能是 Reduxconnect 函數(shù)了,上述的 buildReduxContainer 函數(shù)就是一個簡陋版 connect 函數(shù)。
  • React RouterwithRouter 函數(shù),它從上下文中抓取路由并作為 props 傳入子組件。
  • [react-apollo](http://dev.apollodata.com/react/) 主要的接口就是 graphql HOC,給定一個組件和一個 GraphQL 請求,即為子組件提供請求的返回結(jié)果。
  • Recompose 是一個全是 HOC 的庫,它能執(zhí)行一系列任何你想從組件中抽取出來的不同的子任務(wù)。

自定義 HOC

應(yīng)該為你的應(yīng)用編寫新的 HOC 嗎?當(dāng)然了,如果你有組件的模板要生成的話更應(yīng)該這么做。

以上簡單分享了有用的庫和簡單的組成方式,HOC 是 React 組件中共享行為的最佳方式。

編寫 HOC 是一個函數(shù)返回類的簡單方法,像我們在上面看到的 buildReduxContainer 方法。如果你想了解通過構(gòu)建 HOC 你能做些什么,我建議你閱讀 Fran Guijarro 關(guān)于這一主題的 極度全面的博客。

結(jié)論

高階組件在本質(zhì)上是一種以 函數(shù)式 的方式分離組件中的關(guān)注點(diǎn)的編碼方式。React 早期版本用 class 和 mixin 來重用代碼,但所有跡象表明更函數(shù)式的方法才是 React 的未來。

如果當(dāng)你聽說函數(shù)式編程技術(shù)時呆住了,不要緊!React 團(tuán)隊致力于簡化這些方法,讓我們所有人都能寫出模塊化,組件化的 UI。

如果你想獲取更多關(guān)于構(gòu)建現(xiàn)代、組件化應(yīng)用的信息,查閱我在 Chroma 上的 系列博客。如果你喜歡這篇文章,請點(diǎn)贊?? 并分享出去哦~


掘金翻譯計劃 是一個翻譯優(yōu)質(zhì)互聯(lián)網(wǎng)技術(shù)文章的社區(qū),文章來源為 掘金 上的英文分享文章。內(nèi)容覆蓋 Android、iOSReact、前端、后端產(chǎn)品、設(shè)計 等領(lǐng)域,想要查看更多優(yōu)質(zhì)譯文請持續(xù)關(guān)注 掘金翻譯計劃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,174評論 2 35
  • 我們已經(jīng)詳細(xì)介紹了Action,Reducer,Store和它們之間的流轉(zhuǎn)關(guān)系。Redux的基礎(chǔ)知識差不多也介紹完...
    張歆琳閱讀 3,873評論 1 17
  • 做React需要會什么? react的功能其實(shí)很單一,主要負(fù)責(zé)渲染的功能,現(xiàn)有的框架,比如angular是一個大而...
    蒼都閱讀 14,948評論 1 139
  • 在目前的前端社區(qū),『推崇組合,不推薦繼承(prefer composition than inheritance)...
    Wenliang閱讀 78,003評論 16 124
  • 我今天所說的代價,那天我打開手機(jī)視頻,看到一個欄目聊科技說到人類進(jìn)化中的一個有趣問題。如果人類的醫(yī)生可以任意修改我...
    驛站1124閱讀 165評論 0 1

友情鏈接更多精彩內(nèi)容