這是螞蟻金服內部技術分享的文字稿,本次分享主要介紹了爲何要用 Hooks?以及如何使用 Umi Hooks 提效?react
Umi Hooks github.com/umijs/hooksgit
你們好,我叫盡龍,來自體驗技術部。社區名稱叫 brickspert,磚家的意思。程序員
自從 React 推出 React Hooks 後,就開始嘗試使用 Hooks,並逐漸喜歡上了它。目前,幾乎 100% 的組件都是使用 Hooks 開發。通過大半年的實踐,在 Hooks 使用方面沉澱了一些經驗,很高興今天有機會能分享給你們。github
在分享開始以前,我想了解下:「有多少同窗目前已經在項目中大量使用 Hooks 了?」面試
嗯嗯,謝謝。看舉手的同窗,大概一半一半吧。不要緊,聽完今天的分享,我相信你必定有興趣嘗試下 Hooks 的。redux
React Hooks 是 react v16.8 的一個新特性,很佩服這麼重磅的功能,在一個小版本中發佈,說明 React 團隊有足夠的信心向上兼容。c#
爲何要放棄 Class,轉用 Hooks 呢?在內部外部有不少爭論,包括知乎也有相似提問。咱們也難免俗套的要對比下 Class 和 Hooks 了。固然爲了保證今天的分享效果,我確定會偏向 Hooks 的(哈哈哈哈)。bash
Class 學習成本很高。首當其中的就是生命週期,多,太多了。不只多,還會變!React v15 和 v16 就不同。下面是我在網上隨便找的一張圖。markdown
這個是 React v15 的生命週期,你都掌握了嗎?你知道 v16 有什麼變化嗎?網絡
以前不管你去哪裏面試,基本都會有幾個必問問題:
生命週期最重要,可是有很高的學習成本,須要大量實踐才能積累足夠的經驗。固然,這幾個問題回答很差,百分之八十以上的概率會掛掉。
固然不止是生命週期,this 也是一個很大的問題。你有沒有在組件寫不少 bind
?或者全部的函數都用箭頭函數定義?
this.someFunction = this.someFunction.bind(this);
// 或
someFunction = ()=>{}
複製代碼
爲何要這樣寫呢?若是不寫會有什麼問題?哎呦,又多了一個面試題,你會嗎?
對比 Class,Hooks 的學習成本可就過低了!掌握了 useState 和 useEffect,80% 的事情就搞定了。
Class 業務邏輯分散,實現一個功能,我要寫在不一樣的生命週期裏面,不聚合~
好比,若是你有個定時器,你必定要在 componentWillUnMount
去卸載。
再好比,咱們要寫一個請求用戶信息的組件,當userId
變化時,要從新發起請求。咱們就要在兩個生命中期中寫請求的邏輯。
相信上面的邏輯,你們也是常常會寫的吧。
奧奧,sorry,上面的 componentWillReceiveProps
已經被廢棄了,咱們應該用 componentDidUpdate
來代替。
「咦,這是爲啥呢?好好的爲何要廢棄,不讓這麼用了?」
又來一個面試題!你知道答案嗎?
而 Hooks 的業務邏輯就很是聚合了。上面的兩個例子,改爲 Hooks 你會寫嗎?
簡直不要太簡單!香啊!我能夠提早下班了。
說到邏輯複用,不少同窗會說 Class 的 Render Props 和 HOC(高階組件)能夠作邏輯複用!那咱們看看 Class 的邏輯複用有多麼的慘不忍睹。
首先咱們看看 Render Props。
首先咱們想複用監聽 window size 變化的邏輯,開開心心的寫了下面的代碼。
而後,我又想複用監聽鼠標位置的邏輯,我只能這麼寫了。
到這裏你應該看到了問題所在。這簡直就是地獄!我不忍心複用其它邏輯了。
咱們放過 Render Props,來看看 HOC 吧。
若是你要問什麼是 HOC,那我不得不推薦個人另一篇文章《助你徹底理解React高階組件(Higher-Order Components)》。
哪怕你不知道 HOC 是啥,你也必定用過。好比 redux 的 connect。
上面的代碼,我用了三個 HOC,分別是 redux 的 connect,react-intl 的 injectIntl,以及 AntD 的 Form.create()。
這是一個很是常見的用法。若是你光看代碼,大概已經懵圈了。「我是誰?我在哪?我要幹什麼?」
這會我彷彿聽見 HOC 在說:「我不只讓你看不懂我,我還很容易出各類問題。」
是的,HOC 很容易出問題。你們都往組件的 props 上面掛屬性,萬一有個重名的,那就只能說一句「很差意思,GG思密達」!
Hooks 來了,它表示,我要一個打五個!Render Props 和 HOC 聯合起來也被我秒殺!
Hooks 表示,來十個,來一百個我也能打。
Hooks 最強的能力就是邏輯複用了,這是我最最最愛的能力了。
是的,我也不偏袒 Hooks,因爲 React Hooks 的機制,若是用法不正確,會致使各類奇怪的閉包問題。
若是你要問 React Hooks 的機制是什麼的話,我又要給你推薦一篇我以前寫的文章了:《React Hooks 原理》。
那面對這個問題,怎麼解呢?說實話,我也沒有很好的解決辦法。
可是,這可能也有好處。若是碰到想不明白的問題,那 99% 是因爲閉包致使的,咱們有很肯定的方向去排查問題。
記住這句話,你能夠少走不少彎路。
固然,說再多,吹再好,也沒多大用。我上面講的 Class 和 Hooks 的優缺點,網上的也有不少人講,你們也確定都看過。
用程序員的交流方式,就是「Talk is cheap,Show me the code.」。
亮劍吧!
接下來,我會用一個例子,讓你折服,拜倒在 Hooks 的石榴裙下。若是你不服,我們單獨撕~
接下來,咱們來實現一個最最最多見的組件。該組件接收 userId,而後發起網絡請求,得到用戶信息。
說白了,就是最簡單的發起網絡請求的組件。咱們先用 Class 來實現看看。
這段代碼,是最簡單的網絡請求。
美滋滋。可是少了點東西。網絡請求,咱們確定要維護一個 loading 狀態,保證用戶體驗比較好。
那咱們加上吧。
這張圖,咱們增長了 loading 狀態,在網絡請求發起前,置爲 true,在網絡請求結束後,置爲 false。
美滋滋。可是仍是少點東西。userId 變化後,我要從新發起網絡請求吧。
咱們再加點代碼吧。
咱們增長了對 userId 變化的監聽,若是 userId 變化後,從新發起請求。
此次穩了吧?
不不不,還不夠。若是 userId 連續變化了 5 次,發送了 5 個網絡請求,咱們要保證老是最後一次網絡請求有效。也就是常常說的的「競態處理」或者「時序控制」。
我加!加還不行嗎!
其實到這裏,有些同窗已經懵了。「你說的時序控制,聽着頗有道理,但我平時都沒處理過這個問題,我看下你怎麼實現的。」
確實,時序控制不算一個簡單的問題,不少新手都不會解決這個問題。
穩了!到這裏你以爲穩了吧。
仍是年輕啊,小夥子。
若是用上面的代碼來玩,你可能會偶爾碰到上面的警告。這個警告是怎麼形成的呢?我說一下你就明白了。下面四個步驟執行,必會報警告:
看出問題了嗎?組件已經卸載了,還去 setState,形成了內存溢出。
怎麼解決呢?
在組件卸載的時候,放棄最後一次請求。
到這裏爲止,咱們就完成了一個完美的網絡請求。此次真結束了!
看下寫了多少行代碼。
除去空格,咱們寫了 38 行代碼。實話說,38 行代碼我能忍,可是這些邏輯我忍不了!回想下咱們處理了多少邏輯:
關鍵這些邏輯是沒辦法複用的!每一個項目可能有數十上百個組件會發網絡請求,我就要寫幾十,幾百遍這樣的邏輯。想一想我都難受。
說實話,我在寫項目的時候常常會偷懶。要不就不寫 loading,要不就無論競態,要不就無論最後的內存溢出警告。
你有沒有和我同樣呢?嘿嘿。
言歸正傳,接下來就邀請 Hooks 登場了。
三下五除二,咱們用 Hooks 實現了剛纔全部的邏輯。
17 行!代碼量減小了 50% 以上。好像還行!
可是,別忘了,Hooks 最重要的能力就是邏輯複用!這些邏輯咱們徹底能夠封裝起來!咱們把剛纔的邏輯所有封裝起來!
useAsync 封裝了剛纔咱們說的全部功能,一行代碼完成了網絡請求。
最後整個組件會長這樣。
哇!我本身都佩服本身!簡直了!美呆了,帥斃了,感受本身無敵了!提早完成工做,下班回家!
經過這個例子,我想證實一個論點:「使用 Hooks 封裝邏輯的能力,能夠極大提升開發效率」。
這時候你確定要問,useAsync 在哪裏?給我瞧瞧?
useAsync 在這裏,快來瞧,快來看啦!
useAsync 是 Umi Hooks 庫的核心 Hooks 之一,Umi Hooks 提供了大量提煉自業務的 Hooks。一行代碼真的能夠實現不少功能!
固然,useAsync 不止包含上面說的功能,還支持「手動觸發」執行,還支持「輪詢」!
只要簡單的配置一個 pollingInterval
,就能輪詢發送請求了。快去試試啦!
接下來咱們會介紹幾個更牛的 Hooks 給你們認識!
AntD 的 Table 組件,想必你們在項目中常常用到吧!除了剛纔異步請求的全部邏輯外,你還得處理其它的邏輯。
好比維護 page、pageSize、sorter、filter 的狀態,還得考慮搜索條件變化後,重置 page 到第一頁。這些邏輯光想一想就頭疼了,別說寫了。
如今一行代碼就能夠實現了!useAntdTable,封裝了全部的邏輯,只要一行代碼!如圖上所示,你只要 ...tableProps
,就能夠了。這也許就是幸福的味道吧~
加載更多的場景,好比下面動圖的場景,想必你們在工做中都寫過。
這樣一個加載更多的場景,咱們要維護多少狀態?寫多少行邏輯?原本我打算寫個 Class 實現的例子貼出來的,可是我放棄了,由於太難了~~
隨便想一想要處理的邏輯:
腦袋疼,真的腦袋疼。我會寫,可是寫起來真的好累。
還沒完,通常產品同窗還會要求,上拉加載更多......
這時候咱們還得監聽滾動位置,若是快到底了,觸發加載更多。腦袋更疼了!
Umi Hooks 聽到了你的求救,派出 useLoadMore 來拯救你了。一行代碼就能夠實現全部的功能!一個小時變一分鐘,又能夠早點下班了。
還有更好用的,好比 useDynamicList,下面的動態列表,一行代碼搞定。
不只是上面講到的各類複雜邏輯能夠封裝。簡單的邏輯封裝起來也是極其好用的,好比 Boolean 值的管理。
咱們通常控制 Modal,Popover 等顯示隱藏的時候,都要維護一個 visible 狀態。大概會是這樣。
這樣的邏輯,你寫過多少遍?沒有幾千也有幾百吧!
之後你就能夠用 useBoolean 咯!
不只是上面講到的這些,咱們還有不少不少的 Hooks。
好比 useSearch,就封裝了一般異步搜索場景的邏輯,好比 debounce。
好比 useVirtualList,就封裝了虛擬列表的邏輯。
好比 useMouse,封裝了監聽鼠標位置的邏輯。
好比 useKeyPress,封裝了監聽鍵盤按鍵的邏輯。
30+ Hooks 供您選擇,而且咱們仍然處於嬰兒期,快速發展中。咱們的願景就是:封裝別人的邏輯,讓別人無邏輯可寫。
如上面所述,咱們如今還處於嬰兒期,須要不斷汲取能量,更多的 Hooks 正在路上!要實現「讓別人無邏輯可寫」的目標,還需繼續奮鬥。
你們應該都聽過 useSWR 吧?是 zeit 公司開發的一個專門作網絡請求的 Hooks,提供了不少新穎的思路,給了咱們很是大的啓發,github star 就像坐火箭同樣。但在實際項目使用中,仍是會有不少地方不符合螞蟻內部的體系。可是它給咱們很是大的啓發,基於 swr 的思路,咱們能夠實現更強大的 useRequest!圖上的能力,咱們都要!
useRequest 目前已經處於內測期了,下個版本將會與你們見面!咱們的目標是:全部的網絡請求,只用 useRequest 就夠了!
目前社區上 Hooks 相關的基礎教程、進階教程、原理深刻、常見問題等文檔都比較分散,咱們準備向 Hooks 生態發展,提供各式各樣的文章。之後學習 Hooks,使用 Hooks,找 Umi Hooks 就對了。
固然,生態方面目前正在規劃中,預計年後啓動。
Umi Hooks,你值得擁有。
咱們目前處於發展階段,歡迎你們一塊兒共建。
你能夠提 idea,咱們負責實現。
你能夠提 issue,咱們負責改 bug。
你能夠提 PR,將你封裝的 Hooks 分享給你們,讓更多人收益。
❤️期待您的參與。