Umi Hooks - 助力擁抱 React Hooks

這是螞蟻金服內部技術分享的文字稿,本次分享主要介紹了爲何要用 Hooks?以及如何使用 Umi Hooks 提效?

Umi Hooks http://github.com/umijs/hooksreact

開場

image-20200116191741370

你們好,我叫盡龍,來自體驗技術部。社區名稱叫 brickspert,磚家的意思。git

自從 React 推出 React Hooks 後,就開始嘗試使用 Hooks,並逐漸喜歡上了它。目前,幾乎 100% 的組件都是使用 Hooks 開發。通過大半年的實踐,在 Hooks 使用方面沉澱了一些經驗,很高興今天有機會能分享給你們。程序員

image-20200116191759286

在分享開始以前,我想了解下:「有多少同窗目前已經在項目中大量使用 Hooks 了?」github

嗯嗯,謝謝。看舉手的同窗,大概一半一半吧。不要緊,聽完今天的分享,我相信你必定有興趣嘗試下 Hooks 的。面試

React Hooks 是 react v16.8 的一個新特性,很佩服這麼重磅的功能,在一個小版本中發佈,說明 React 團隊有足夠的信心向上兼容。redux

Why Hooks?

image-20200116191817331

爲何要放棄 Class,轉用 Hooks 呢?在內部外部有不少爭論,包括知乎也有相似提問。咱們也難免俗套的要對比下 Class 和 Hooks 了。固然爲了保證今天的分享效果,我確定會偏向 Hooks 的(哈哈哈哈)。c#

image-20200116192324872

Class 學習成本高

Class 學習成本很高。首當其中的就是生命週期,多,太多了。不只多,還會變!React v15 和 v16 就不同。下面是我在網上隨便找的一張圖。網絡

image-a5b927b35025

這個是 React v15 的生命週期,你都掌握了嗎?你知道 v16 有什麼變化嗎?閉包

以前不管你去哪裏面試,基本都會有幾個必問問題:異步

  • 講講 React 生命週期?React v15 和 React v16 生命週期有啥變化?
  • 如何優化 Class 組件?shouldComponentUpdate 是作什麼的?如何用?
  • 通常在哪一個生命週期發送網絡請求?爲何?
  • ......

生命週期最重要,可是有很高的學習成本,須要大量實踐才能積累足夠的經驗。固然,這幾個問題回答很差,百分之八十以上的概率會掛掉。

固然不止是生命週期,this 也是一個很大的問題。你有沒有在組件寫不少 bind?或者全部的函數都用箭頭函數定義?

this.someFunction = this.someFunction.bind(this);

// 或
someFunction = ()=>{}

爲何要這樣寫呢?若是不寫會有什麼問題?哎呦,又多了一個面試題,你會嗎?

Hooks 學習成本低

對比 Class,Hooks 的學習成本可就過低了!掌握了 useState 和 useEffect,80% 的事情就搞定了。

image-7cbf7879e7cf

Class 業務邏輯分散

Class 業務邏輯分散,實現一個功能,我要寫在不一樣的生命週期裏面,不聚合~

好比,若是你有個定時器,你必定要在 componentWillUnMount 去卸載。

image-67b7d915f6af

再好比,咱們要寫一個請求用戶信息的組件,當userId 變化時,要從新發起請求。咱們就要在兩個生命中期中寫請求的邏輯。

image-4059e72aa129

相信上面的邏輯,你們也是常常會寫的吧。

奧奧,sorry,上面的 componentWillReceiveProps 已經被廢棄了,咱們應該用 componentDidUpdate 來代替。

「咦,這是爲啥呢?好好的爲何要廢棄,不讓這麼用了?」

又來一個面試題!你知道答案嗎?

Hooks 業務邏輯聚合

而 Hooks 的業務邏輯就很是聚合了。上面的兩個例子,改爲 Hooks 你會寫嗎?

image-3780dc60b735

image-88e9ba8a7add

簡直不要太簡單!香啊!我能夠提早下班了。

Class 邏輯複用困難

說到邏輯複用,不少同窗會說 Class 的 Render Props 和 HOC(高階組件)能夠作邏輯複用!那咱們看看 Class 的邏輯複用有多麼的慘不忍睹。

首先咱們看看 Render Props。

首先咱們想複用監聽 window size 變化的邏輯,開開心心的寫了下面的代碼。

image-f9273eefa2ef

而後,我又想複用監聽鼠標位置的邏輯,我只能這麼寫了。

image-d60b6492b570

到這裏你應該看到了問題所在。這簡直就是地獄!我不忍心複用其它邏輯了。

咱們放過 Render Props,來看看 HOC 吧。

若是你要問什麼是 HOC,那我不得不推薦個人另一篇文章《 助你徹底理解React高階組件(Higher-Order Components)》。

哪怕你不知道 HOC 是啥,你也必定用過。好比 redux 的 connect。

image-20200116200932301

上面的代碼,我用了三個 HOC,分別是 redux 的 connect,react-intl 的 injectIntl,以及 AntD 的 Form.create()。

這是一個很是常見的用法。若是你光看代碼,大概已經懵圈了。「我是誰?我在哪?我要幹什麼?」

這會我彷彿聽見 HOC 在說:「我不只讓你看不懂我,我還很容易出各類問題。」

是的,HOC 很容易出問題。你們都往組件的 props 上面掛屬性,萬一有個重名的,那就只能說一句「很差意思,GG思密達」!

Hooks 邏輯複用簡單

Hooks 來了,它表示,我要一個打五個!Render Props 和 HOC 聯合起來也被我秒殺!

image-5a6f5d648ca9

Hooks 表示,來十個,來一百個我也能打。

Hooks 最強的能力就是邏輯複用了,這是我最最最愛的能力了。

Hooks 會產生不少閉包問題

是的,我也不偏袒 Hooks,因爲 React Hooks 的機制,若是用法不正確,會致使各類奇怪的閉包問題。

若是你要問 React Hooks 的機制是什麼的話,我又要給你推薦一篇我以前寫的文章了:《 React Hooks 原理》。

那面對這個問題,怎麼解呢?說實話,我也沒有很好的解決辦法。

可是,這可能也有好處。若是碰到想不明白的問題,那 99% 是因爲閉包致使的,咱們有很肯定的方向去排查問題。

image-4636b47be14f

記住這句話,你能夠少走不少彎路。

Show Case

image-20200116203233594

固然,說再多,吹再好,也沒多大用。我上面講的 Class 和 Hooks 的優缺點,網上的也有不少人講,你們也確定都看過。

用程序員的交流方式,就是「Talk is cheap,Show me the code.」。

亮劍吧!

接下來,我會用一個例子,讓你折服,拜倒在 Hooks 的石榴裙下。若是你不服,我們單獨撕~

網絡請求組件實現

image-20200116214124140

接下來,咱們來實現一個最最最多見的組件。該組件接收 userId,而後發起網絡請求,得到用戶信息。

說白了,就是最簡單的發起網絡請求的組件。咱們先用 Class 來實現看看。

image-20200116214639300

這段代碼,是最簡單的網絡請求。

  • 定義一個 username 狀態。
  • componentDidMount 的時候發起網絡請求。
  • 網絡請求結束,更新 username。

美滋滋。可是少了點東西。網絡請求,咱們確定要維護一個 loading 狀態,保證用戶體驗比較好。

那咱們加上吧。

image-20200116214918755

這張圖,咱們增長了 loading 狀態,在網絡請求發起前,置爲 true,在網絡請求結束後,置爲 false。

美滋滋。可是仍是少點東西。userId 變化後,我要從新發起網絡請求吧。

咱們再加點代碼吧。

image-20200116215101730

咱們增長了對 userId 變化的監聽,若是 userId 變化後,從新發起請求。

此次穩了吧?

不不不,還不夠。若是 userId 連續變化了 5 次,發送了 5 個網絡請求,咱們要保證老是最後一次網絡請求有效。也就是常常說的的「競態處理」或者「時序控制」。

我加!加還不行嗎!

image-20200116215409524

其實到這裏,有些同窗已經懵了。「你說的時序控制,聽着頗有道理,但我平時都沒處理過這個問題,我看下你怎麼實現的。」

確實,時序控制不算一個簡單的問題,不少新手都不會解決這個問題。

穩了!到這裏你以爲穩了吧。

仍是年輕啊,小夥子。

image-20200116220003295

若是用上面的代碼來玩,你可能會偶爾碰到上面的警告。這個警告是怎麼形成的呢?我說一下你就明白了。下面四個步驟執行,必會報警告:

  1. 組件加載
  2. 發起網絡請求
  3. 組件卸載
  4. 網絡請求請求成功,觸發 setState

看出問題了嗎?組件已經卸載了,還去 setState,形成了內存溢出。

怎麼解決呢?

image-20200116220311200

在組件卸載的時候,放棄最後一次請求。

到這裏爲止,咱們就完成了一個完美的網絡請求。此次真結束了!

看下寫了多少行代碼。

image-20200116220531399

除去空格,咱們寫了 38 行代碼。實話說,38 行代碼我能忍,可是這些邏輯我忍不了!回想下咱們處理了多少邏輯:

  • 網絡請求
  • loading
  • userId 變化從新發起請求
  • 競態處理
  • 組件卸載放棄網絡請求

關鍵這些邏輯是沒辦法複用的!每一個項目可能有數十上百個組件會發網絡請求,我就要寫幾十,幾百遍這樣的邏輯。想一想我都難受。

說實話,我在寫項目的時候常常會偷懶。要不就不寫 loading,要不就無論競態,要不就無論最後的內存溢出警告。

你有沒有和我同樣呢?嘿嘿。

言歸正傳,接下來就邀請 Hooks 登場了。

image-20200116221123031

三下五除二,咱們用 Hooks 實現了剛纔全部的邏輯。

image-20200116221212124

17 行!代碼量減小了 50% 以上。好像還行!

可是,別忘了,Hooks 最重要的能力就是邏輯複用!這些邏輯咱們徹底能夠封裝起來!咱們把剛纔的邏輯所有封裝起來!

image-20200116221359407

useAsync 封裝了剛纔咱們說的全部功能,一行代碼完成了網絡請求。

最後整個組件會長這樣。

image-20200116221605411

哇!我本身都佩服本身!簡直了!美呆了,帥斃了,感受本身無敵了!提早完成工做,下班回家!

image-20200116221755193

經過這個例子,我想證實一個論點:「使用 Hooks 封裝邏輯的能力,能夠極大提升開發效率」。

Umi Hooks

這時候你確定要問,useAsync 在哪裏?給我瞧瞧?

image-20200116221941442

useAsync

useAsync 在這裏,快來瞧,快來看啦!

useAsync 是 Umi Hooks 庫的核心 Hooks 之一,Umi Hooks 提供了大量提煉自業務的 Hooks。一行代碼真的能夠實現不少功能!

Umi Hooks 在這裏在這裏!你懂的~~

image-20200116222352082

固然,useAsync 不止包含上面說的功能,還支持「手動觸發」執行,還支持「輪詢」!

只要簡單的配置一個 pollingInterval ,就能輪詢發送請求了。快去試試啦

接下來咱們會介紹幾個更牛的 Hooks 給你們認識!

useAntdTable

image-cc2f4b087aca

AntD 的 Table 組件,想必你們在項目中常常用到吧!除了剛纔異步請求的全部邏輯外,你還得處理其它的邏輯。

image-20200116232857059

好比維護 page、pageSize、sorter、filter 的狀態,還得考慮搜索條件變化後,重置 page 到第一頁。這些邏輯光想一想就頭疼了,別說寫了。

如今一行代碼就能夠實現了!useAntdTable,封裝了全部的邏輯,只要一行代碼!如圖上所示,你只要 ...tableProps,就能夠了。這也許就是幸福的味道吧~

useLoadMore

加載更多的場景,好比下面動圖的場景,想必你們在工做中都寫過。

image-22fa47992b6f

這樣一個加載更多的場景,咱們要維護多少狀態?寫多少行邏輯?原本我打算寫個 Class 實現的例子貼出來的,可是我放棄了,由於太難了~~

隨便想一想要處理的邏輯:

  • 第一次加載時候的 loading
  • 加載更多時候的 loading
  • 維護 page 和 pageSize
  • 網絡請求
  • 是否是加載全了
  • 搜索條件變化後,重置到第一頁。
  • .....

腦袋疼,真的腦袋疼。我會寫,可是寫起來真的好累。

還沒完,通常產品同窗還會要求,上拉加載更多......

image-cf629db68ebc

這時候咱們還得監聽滾動位置,若是快到底了,觸發加載更多。腦袋更疼了!

image-20200116235013101

Umi Hooks 聽到了你的求救,派出 useLoadMore 來拯救你了。一行代碼就能夠實現全部的功能!一個小時變一分鐘,又能夠早點下班了。

useDynamicList

image-20200116235455431

還有更好用的,好比 useDynamicList,下面的動態列表,一行代碼搞定。

image-16556dfcf0e8

useBoolean

不只是上面講到的各類複雜邏輯能夠封裝。簡單的邏輯封裝起來也是極其好用的,好比 Boolean 值的管理。

咱們通常控制 Modal,Popover 等顯示隱藏的時候,都要維護一個 visible 狀態。大概會是這樣。

image-20200116235651552

這樣的邏輯,你寫過多少遍?沒有幾千也有幾百吧!

image-20200116235850057

之後你就能夠用 useBoolean 咯!

More

image-20200116235957789

不只是上面講到的這些,咱們還有不少不少的 Hooks。

好比 useSearch,就封裝了一般異步搜索場景的邏輯,好比 debounce。

好比 useVirtualList,就封裝了虛擬列表的邏輯。

image-20200117000249183

好比 useMouse,封裝了監聽鼠標位置的邏輯。

好比 useKeyPress,封裝了監聽鍵盤按鍵的邏輯。

image-20200117000412951

30+ Hooks 供您選擇,而且咱們仍然處於嬰兒期,快速發展中。咱們的願景就是:封裝別人的邏輯,讓別人無邏輯可寫。

將來規劃

image-20200117001130397

更多的 Hooks 開發

如上面所述,咱們如今還處於嬰兒期,須要不斷汲取能量,更多的 Hooks 正在路上!要實現「讓別人無邏輯可寫」的目標,還需繼續奮鬥。

更強大的 useRequest

image-20200117001209280

你們應該都聽過 useSWR 吧?是 zeit 公司開發的一個專門作網絡請求的 Hooks,提供了不少新穎的思路,給了咱們很是大的啓發,github star 就像坐火箭同樣。但在實際項目使用中,仍是會有不少地方不符合螞蟻內部的體系。可是它給咱們很是大的啓發,基於 swr 的思路,咱們能夠實現更強大的 useRequest!圖上的能力,咱們都要!

useRequest 目前已經處於內測期了,下個版本將會與你們見面!咱們的目標是:全部的網絡請求,只用 useRequest 就夠了!

Hooks 生態

目前社區上 Hooks 相關的基礎教程、進階教程、原理深刻、常見問題等文檔都比較分散,咱們準備向 Hooks 生態發展,提供各式各樣的文章。之後學習 Hooks,使用 Hooks,找 Umi Hooks 就對了。

固然,生態方面目前正在規劃中,預計年後啓動。

總結

image-20200117001711649

Umi Hooks,你值得擁有。

咱們目前處於發展階段,歡迎你們一塊兒共建。

你能夠提 idea,咱們負責實現。

你能夠提 issue,咱們負責改 bug。

你能夠提 PR,將你封裝的 Hooks 分享給你們,讓更多人收益。

❤️期待您的參與。

相關文章
相關標籤/搜索