React Hooks 已經出來有段時間了, 不少小夥伴或多或少都用過。javascript
今天呢,咱們就回頭再看一下這個東西,思考一下,這個東西爲何會出現,它解決了什麼問題, 以及背後的設計理念。html
若是你有 Hooks 的使用經驗, 能夠思考一下這兩個問題:java
咱們先主要圍繞這兩點展開討論。react
在正式開始這個話題前, 咱們先回顧一下react的發家史
.app
2013年5月13號, 在JS Conf 上發佈了第一個版本0.3.0
.async
我第一次接觸React 是在2015年, 對createClass語法記憶猶新:ide
createClass
是當時第一種用於建立 React 組件的語法, 由於當時Javascript 尚未成形的 Class 體系。函數
這種狀況在2015年1月17號獲得了改變。post
這時候, ES6 正式發佈,支持 Class 語法。ui
這時候面臨一個選擇:
繼續用自家的 createClass
呢 仍是使用新的 ES6 Class
?
畢竟 createClass 又不是不能用, 並且用着還挺順手。
最後, React 仍是選擇了擁抱新趨勢, 使用ES6 Class。
並在 React 0.13.1 Beta1
版本, 開始支持使用原生Javasciprt Class語法。 我找到了以下說明:
大意就是: 咱們並不想本身單獨搞一套, 你們習慣怎麼用, 咱們就怎麼搞。
基於這個變化, React Class 組件就變成了咱們以前常常見到的這樣:
是否是很熟悉。
生命週期方法和以前保持一致
,變化的是組件初始化
的部分。
從本來的getInitinalState
變成了constructor
。
經歷過這個階段的小夥伴確定對如下代碼段很是熟悉:
constructor(props) { super(props); // 🤮🤮🤮 this.state = {}; this.xxx = this.xxx.bind(this); // 😢😢😢 }
在組件初始化的時候, 必須手動super(props)
一下, 至於爲何這麼作, 本文不作討論, 有興趣的能夠看一下這篇譯文: 爲何要寫Super(props)。
Class Fields
也容許咱們跳過這一步:
class xxx extends React.Component { state = {}; }
到這一步, 已經解決了兩個使人難受的點:
已經足夠OK了, 是吧。
其實還不夠。
咱們在編寫react 應用的時候, 難以免的一件事就是: 拆分react 組件。
把一個複雜的UI視圖拆分
成不一樣的模塊, 而後組合
在一塊兒。
這也是 react 自己推崇的理念: 萬物皆但是組件。
這種設計很棒棒, 但依舊有不少問題。
我認爲主要是亮點:
組件內邏輯的割裂
邏輯複用困難
邏輯上的割裂
:基於生命週期的設計, 使得咱們常常寫出邏輯割裂
的代碼:
一樣的邏輯, 咱們須要在不一樣的生命週期中去實現。
在一個大型的app 中, 相似的邏輯會有不少, 摻雜在一塊兒。
後人要去修改的時候, 不得不使用上下左右反覆橫跳之術
, 使人十分痛苦。
邏輯複用困難
咱們都知道, react 應用實際上是由一些列 UI 套件組合而成的, 這些套件有些有狀態, 有些沒有狀態。
把這些組件組合在一塊兒,處理好複用, 實際上是有必定困難的。
好比,假設在另一個組件,有和上圖類似的邏輯, 怎麼辦呢?
Copy & Paste
顯然是能夠的, 但卻不是最優雅的。
React 自己並不提供解決方案,可是機智的網友們
逐漸摸索出了一些改善這個問題的方法:
High Order Components
Render Props
以High Order Components
爲例, 看一下最簡的例子
爲組件都加入一個data屬性, 而後返回這個加強的組件:
邏輯並不複雜。
回到咱們最初的那個例子, 如今要把這部分邏輯抽離出來, 實現一個WithRepos
高階方法:
使用的時候, 包裹一下就能夠了:
Render Props 也是一樣的目的, 不做贅述, 可參考:Render Props
這兩種作法, 均可以改善邏輯複用的困境,但同時又引入了新的問題。
仍是以高級組件爲例, 好比咱們對一個組件要加入多個加強功能,顯而易見, 代碼就變成了:
export default withA( withB ( withC ( withD ( Component ) ) ) )
Render Props 也同樣, 這兩種模式都會限制你的組件結構,隨着功能的增長, 包裹的層數愈來愈多,陷入所謂的 wrapper hell
之中。
這種狀況並非咱們想要的。
寫到這裏, 咱們進行一個簡單的總結, 整理一下遇到的問題:
因此, 迫切須要一種新的模式
來解決以上這些問題。
理想中, 這種模式要具有如下特色:
那麼, 這種新的模式
該如何設計呢?
此處引用一下John Carmack的話:
並且, Javascript 自己對 function 也是天生友好。
因此, 這時候要作的就是:
在這個背景下, Hooks 應運而生。
擁抱 Function, 面前就有三座大山須要解決:
State Hook 的標準形式是返回一個元組, 包含兩個元素:
使用起來也很是的簡單:
至此,有了state hook, function 就具有了基礎的狀態管理能力:
這一步, 咱們先忘記傳統 Class Component 的生命週期方法, 想一下, 如何在 Function 中實現相似的能力。
咱們要用這樣的能力去實現,好比:
基於這樣的思考, useEffect 問世了。
useEffect 賦予了Function 在組件內部執行反作用的能力。
就形式而言, Effect Hook 接受兩個參數:
簡單的例子:
當 username 變化時, 就修改document title.
⚠️ 注意
有時候,你也許會不經意間把 Effect 寫成一個 async 函數:
強烈建議你不要這樣作。
useEffect 約定:
Effect 函數要麼沒有返回值
,要麼返回一個 Cleanup 函數
。
而這裏 async 函數會隱式地返回一個 Promise,直接違反了這一約定,會形成不可預測的結果
。
至此, Function 組件也有了應該具有的生命週期方法。
只剩最後一個課題: 邏輯複用。
傳統而言, 咱們把頁面拆分紅一個個UI組件, 而後把這個UI組件組合起來。 這種狀況最終也不可避免的誕生了 HOC & Render Props 等模式來改善邏輯複用問題。
你可能會想, React Hooks 可能會有新的解決辦法。
辦法的確是有, 它就是Custom Hooks
.
你能夠把須要複用的邏輯抽成一個個單獨的Custom Hook
, 在須要用的地方使用。
舉個例子:
把須要複用的邏輯抽離:
在須要用到的地方使用:
這樣, 咱們就輕鬆而又天然的實現了邏輯的複用。
至此, 三個難題得以解決。
回頭咱們再看這個問題, 其實從始至終, 要解決的問題只有一個:
提高代碼複用以及組合的能力。
順帶的, 也必定程度上提高了組件的內聚性, 減小了維護成本:
相關的邏輯都在單獨的一塊, 改需求的時候,不用須要施展上下左右反覆橫跳之術,提前了下班時間, 多好。
知其然,也要知其因此然。
咱們在本身平時的搬磚運動中, 也要考慮本身的代碼是否具有一下能力:
不坑本身, 也不坑別人, 早點下班。
好了, 別的就不扯了, 但願這篇文章能給你一些啓發和思考。
才疏學淺, 文章如有錯誤, 歡迎留言之正。