- 原文地址:Understanding Higher Order Components
- 原文作者:Tom Coleman
- 譯文出自:掘金翻譯計劃
- 譯者:Haichao Jiang
- 校對者:sun, xilihuasi
理解快速變化的 React 最佳實踐
如果你剛開始接觸 React起胰,你可能已經(jīng)聽說過 “高階組件” 和 “容器” 組件阳掐。你也許會奇怪這都什么鬼東西蜕窿±杼模或者你已經(jīng)開始使用庫提供的 API 了赊舶,但對于這些個術語還有些疑惑。
作為 Apollo 的 React 集成 - 一個重度使用高階組件的熱門開源庫 - 的維護者和文檔作者,我花了些時間來理清這些概念潮模。
我希望這篇文章能夠幫你對這一主題有更進一步的了解。
重識 React
本文假定你已對 React 有一定的了解 - 如果沒有的話有很多資料可供查閱痴施。例如 Sacha Greif 的 React 5 大概念 就是很好的入門文章擎厢。但是,讓我們再回顧一下然后繼續(xù)我們的文章辣吃。
一個 React 應用包含一系列 組件动遭。組件中會傳遞一組輸入屬性(props),并且輸出屏幕渲染的 HTML 片段神得。當一個組件的 props 更新時厘惦,會觸發(fā)組件重繪,HTML 也會相應變化哩簿。
當用戶通過一種事件(例如鼠標點擊)與 HTML 進行交互時宵蕉,組件處理事件要么通過觸發(fā) 回調 prop,要么通過更新內部 state节榜。更新內部 state 也會造成組件自身及其子組件的重繪羡玛。
這里就不得不提組件 生命周期,即組件首次渲染宗苍,綁定 DOM稼稿,傳遞新 props 等亿遂。
組件的渲染函數(shù)返回一個或多個其他組件的實例。合成 視圖樹 是一個好的思維模型渺杉,能夠表明應用內的組件是如何交互的。通常挪钓,組件交互是通過傳遞 props 給子組件實現(xiàn)的是越,或者通過觸發(fā)父組件傳遞來的回調函數(shù)實現(xiàn)。
React 視圖樹中的數(shù)據(jù)流
React UI vs 無狀態(tài)
似乎現(xiàn)在已經(jīng)過時碌上,但曾經(jīng)一切都區(qū)分為 Model倚评,View 和 Controller(或者 View Model,或者 Presenter)來描述馏予。在這種分類方式天梧,View 的任務就是 渲染 并且處理用戶交互,Controller 的任務則是 準備數(shù)據(jù)霞丧。
React 最近的趨勢是實現(xiàn) 無狀態(tài)函數(shù)組件呢岗。這些簡單的“純”組件只根據(jù)自身的 props 轉換成 HTML 和調用回調 props 來響應用戶交互:
他們是函數(shù)式的,你甚至可以就把他們當做函數(shù)蛹尝。如果你的視圖樹包含“純”組件后豫,你可以把整棵樹看成一個由許多小函數(shù)組成的輸出 HTML 的大型函數(shù)。
無狀態(tài)函數(shù)式組件有個很好的特點是極容易測試突那,并且易于理解挫酿。即易于開發(fā)和快速 debug。
但是你不能一直逃避的是愕难,UI 需要狀態(tài)早龟。比如,當用戶滑過菜單時猫缭,要自動打開(我希望是不要啦4械堋)- 在 React 是利用 state 來實現(xiàn)的。要用 state饵骨,你就要用基于 class 的組件翘悉。
把 UI 的 “全局 state” 引入視圖樹就是事情復雜的開始。
全局 State
UI 的 全局 state 不能直接獨立和某個獨立組件相聯(lián)系居触。典型地妖混,這一般包含了兩類事情:
應用的 數(shù)據(jù) 從 server 來。通常轮洋,數(shù)據(jù)用于多處制市,所以并不唯一關聯(lián)某個組件。
全局 UI state弊予,(像 URL祥楣,決定了用戶瀏覽的頁面路徑)。
安置全局 state 的一個方法是應用內綁定最高層的 “根” 組件,并且下發(fā)到各個需要它的子組件中去误褪。然后 state 的改變再通過一連串的回調反饋到頂層责鳍。
單容器從 store 到視圖樹的數(shù)據(jù)流。
這一方法即使快但很笨拙兽间。根組件需要理解全樹的需求历葛,每個子樹的父組件同樣需要理解每個子樹的需求。此時引入另一個概念嘀略。
容器和展示類組件
這個問題通常通過允許任何層級組件都能獲取全局 state 的方式來解決(要求有一些限制)恤溶。
在 React 的世界里,組件可以分為能拿到全局 state 的和不能拿到的帜羊。
“純”組件易于測試和理解(尤其是無狀態(tài)函數(shù)式組件)咒程。一旦一個組件是“不純”的,它就被污染了讼育,并且很難處理帐姻。
因此,出現(xiàn)了一個 pattern 把“不純”的組件拆分成 兩個 組件:
- 容器 組件操作“臟”全局 state
- 展示 組件相反
我們只要像對待上面的一般組件一樣對待展示類組件窥淆,但把臟的和復雜數(shù)據(jù)操作類的工作獨立到容器組件里卖宠。
多容器的數(shù)據(jù)流
容器
一旦你開始區(qū)分展示類/容器類組件,編寫容器組件會變得有趣忧饭。
有件事要注意的是容器類組件有時候不像個組件扛伍。它們可能:
- 獲取并傳遞一個全局 state(可以是 Redux)片段到子組件。
- 運行一個數(shù)據(jù)訪問(可以是 GraphQL)請求词裤,然后把結果傳給子組件刺洒。
當然,如果我們遵循好的拆分原則吼砂,容器 只掛載單個子組件逆航。容器和子組件強綁定,因為子組件天生在 render 方法里渔肩。不是么因俐?
容器歸納
對于容器組件的 眾多類型 來說(例如,某個容器組件訪問的是 Redux store)周偎,實現(xiàn)基本相同抹剩,不同在于細節(jié):渲染的子組件的不同,獲取數(shù)據(jù)的不同蓉坎。
舉個栗子澳眷,在 Redux 的世界里,容器可能是這樣的:
雖然這個容器很多功能不像真的 Redux 容器蛉艾,你可以看到除了 mapStateToProps
的實現(xiàn)和我們包裝的特定 MyComponent
钳踊,每次寫訪問 Redux 的容器衷敌,我們還要寫很多模板代碼。
生成容器
事實上拓瞪,寫一個自動 生成 容器組件的方法會更容易缴罗,這個方法基于相關信息(此例中是子組件和 mapStateToProps
函數(shù))。
這是一個 高階組件(HOC)祭埂,是以子組件和其他選項作為參數(shù)瞒爬,為該子組件構造容器的函數(shù)。
“高階”即“高階函數(shù)” - 構造函數(shù)的函數(shù)沟堡,事實上,可以認為 React 組件是產(chǎn)出 UI 的組件矢空。尤其在無狀態(tài)函數(shù)式組件中航罗,這一方法尤其實用,但是仔細想想屁药,它在純狀態(tài)展示組件中也同樣實用粥血。HOC 其實就是高階函數(shù)。
HOC 例子
這里有些值得一看的例子:
- 最普遍的可能是 Redux 的
connect
函數(shù)了酿箭,上述的buildReduxContainer
函數(shù)就是一個簡陋版connect
函數(shù)复亏。 -
React Router 的
withRouter
函數(shù),它從上下文中抓取路由并作為 props 傳入子組件缭嫡。 -
[react-apollo](http://dev.apollodata.com/react/)
主要的接口就是graphql
HOC缔御,給定一個組件和一個 GraphQL 請求,即為子組件提供請求的返回結果妇蛀。 - Recompose 是一個全是 HOC 的庫耕突,它能執(zhí)行一系列任何你想從組件中抽取出來的不同的子任務。
自定義 HOC
應該為你的應用編寫新的 HOC 嗎评架?當然了眷茁,如果你有組件的模板要生成的話更應該這么做。
以上簡單分享了有用的庫和簡單的組成方式纵诞,HOC 是 React 組件中共享行為的最佳方式上祈。
編寫 HOC 是一個函數(shù)返回類的簡單方法,像我們在上面看到的 buildReduxContainer
方法浙芙。如果你想了解通過構建 HOC 你能做些什么登刺,我建議你閱讀 Fran Guijarro 關于這一主題的 極度全面的博客。
結論
高階組件在本質上是一種以 函數(shù)式 的方式分離組件中的關注點的編碼方式茁裙。React 早期版本用 class 和 mixin 來重用代碼塘砸,但所有跡象表明更函數(shù)式的方法才是 React 的未來。
如果當你聽說函數(shù)式編程技術時呆住了晤锥,不要緊掉蔬!React 團隊致力于簡化這些方法廊宪,讓我們所有人都能寫出模塊化,組件化的 UI女轿。
如果你想獲取更多關于構建現(xiàn)代箭启、組件化應用的信息,查閱我在 Chroma 上的 系列博客蛉迹。如果你喜歡這篇文章傅寡,請點贊?? 并分享出去哦~
掘金翻譯計劃 是一個翻譯優(yōu)質互聯(lián)網(wǎng)技術文章的社區(qū),文章來源為 掘金 上的英文分享文章北救。內容覆蓋 Android荐操、iOS、React珍策、前端托启、后端、產(chǎn)品攘宙、設計 等領域屯耸,想要查看更多優(yōu)質譯文請持續(xù)關注 掘金翻譯計劃。