高階函數(shù)一點通


理解快速變化的 React 最佳實踐

image.png

如果你剛開始接觸 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)。

image.png

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 來響應用戶交互:

image.png

他們是函數(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)系居触。典型地妖混,這一般包含了兩類事情:

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

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

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

image.png

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

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

容器和展示類組件

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

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

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

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

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

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

image.png

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

容器

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

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

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

當然,如果我們遵循好的拆分原則吼砂,容器 只掛載單個子組件逆航。容器和子組件強綁定,因為子組件天生在 render 方法里渔肩。不是么因俐?

容器歸納

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

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

image.png

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

生成容器

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

image.png

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

“高階”即“高階函數(shù)” - 構造函數(shù)的函數(shù)沟堡,事實上,可以認為 React 組件是產(chǎn)出 UI 的組件矢空。尤其在無狀態(tài)函數(shù)式組件中航罗,這一方法尤其實用,但是仔細想想屁药,它在純狀態(tài)展示組件中也同樣實用粥血。HOC 其實就是高階函數(shù)。

HOC 例子

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

  • 最普遍的可能是 Reduxconnect 函數(shù)了酿箭,上述的 buildReduxContainer 函數(shù)就是一個簡陋版 connect 函數(shù)复亏。
  • React RouterwithRouter 函數(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荐操、iOSReact珍策、前端托启、后端產(chǎn)品攘宙、設計 等領域屯耸,想要查看更多優(yōu)質譯文請持續(xù)關注 掘金翻譯計劃

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蹭劈,一起剝皮案震驚了整個濱河市疗绣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铺韧,老刑警劉巖多矮,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哈打,居然都是意外死亡工窍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門前酿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來患雏,“玉大人,你說我怎么就攤上這事罢维⊙吐兀” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵肺孵,是天一觀的道長匀借。 經(jīng)常有香客問我,道長平窘,這世上最難降的妖魔是什么吓肋? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮瑰艘,結果婚禮上是鬼,老公的妹妹穿的比我還像新娘肤舞。我一直安慰自己,他們只是感情好均蜜,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布李剖。 她就那樣靜靜地躺著,像睡著了一般囤耳。 火紅的嫁衣襯著肌膚如雪篙顺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天充择,我揣著相機與錄音德玫,去河邊找鬼。 笑死椎麦,一個胖子當著我的面吹牛化焕,可吹牛的內容都是我干的。 我是一名探鬼主播铃剔,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼查刻!你這毒婦竟也來了键兜?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤穗泵,失蹤者是張志新(化名)和其女友劉穎普气,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佃延,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡现诀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了履肃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仔沿。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖尺棋,靈堂內的尸體忽然破棺而出封锉,到底是詐尸還是另有隱情,我是刑警寧澤膘螟,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布成福,位于F島的核電站,受9級特大地震影響荆残,放射性物質發(fā)生泄漏奴艾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一内斯、第九天 我趴在偏房一處隱蔽的房頂上張望蕴潦。 院中可真熱鬧像啼,春花似錦、人聲如沸品擎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萄传。三九已至甚颂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秀菱,已是汗流浹背振诬。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留衍菱,地道東北人赶么。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像脊串,于是被迫代替她去往敵國和親辫呻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容