- React Hooks開發實戰
- 鬼哥
- 3245字
- 2023-03-30 22:02:25
1.1 React Hooks概述
Hooks是React官方團隊在React 16.8版本中正式引入的概念。通俗地講,Hooks只是一些函數,Hooks可以用于在函數組件中引入狀態管理和生命周期方法。如果希望讓React函數組件擁有狀態管理和生命周期方法,我們不需要再去將React函數組件重構成React類組件,而可以直接使用React Hooks。
值得注意的是,與原來React Class的組件不同,React Hooks在類中是不起作用的。React不僅提供了一些內置的Hooks,比如useState,還支持自定義Hooks,用于管理重復組件之間的狀態。
1.1.1 React Hooks的優點
React Hooks具有以下優點。
1.簡潔
為了更好地體現React Hooks的簡潔,我們分別使用React Class的方式和React Hooks的方式來實現一個點擊按鈕切換字體顏色的小功能。
使用React Class的方式的語法實現如下。

上述代碼通過在state中設置一個顏色狀態屬性來存儲div內字體的顏色,當點擊按鈕的時候,取反設置color狀態的值即可實現我們期望的功能。
下面來看看,同樣的功能使用React Hooks的方式是怎樣實現的。


從這個小例子中,你感受到React Hooks的簡單之處了嗎?相比React Class,使用React Hooks的方式不僅代碼量幾乎減半,而且代碼的可讀性也更勝一籌。
上面兩段代碼實現的功能如圖1-1所示。

圖1-1 改變顏色的示例
2.上手非常簡單
筆者第一次看React Class教程時被嚇到了,感覺上手太難,但由于React是前端從業者必須掌握的技能,所以只能啃下這塊硬骨頭。
React Class上手難表現為如下幾點。
□生命周期函數難以理解,很難熟練掌握。
□與Redux狀態管理相關的概念太多。
□高階組件(HOC)很難理解。
和React Class相比,React Hooks的出現讓React的學習成本降低了很多。具體體現在如下幾點。
□基于函數式編程理念,門檻比較低,只需要掌握一些JavaScript基礎知識。
□與生命周期相關的知識不用學,React Hooks使用全新的理念來管理組件的運作過程。
□與HOC相關的知識不用學,React Hooks能夠完美解決HOC想要解決的問題,并且更可靠。
□MobX取代了Redux來做狀態管理。
3.代碼可復用性更好
如果在大型項目中用React,你會發現項目中的很多React組件冗長且難以復用,尤其是那些寫成Class的組件,它們本身包含狀態(state),使得對它們的復用十分麻煩。
雖然HOC也能解決這個問題,但是它會讓你的組件變得非常復雜。如果你的項目過大或者業務復雜,HOC會導致你的組件難以理解和維護。
在實際項目中,React Hooks可以幫助復用一個有狀態的組件,而且比較簡單。為了更好地體現React Hooks的代碼可復用性好,我們分別使用React Class方式和React Hooks方式以復用組件的方式來實現一個用戶信息展示組件。
使用React Class實現組件的復用需要借助HOC。HOC的使用方法不在本書范圍內,此處不做介紹,大家可以自行查閱相關資料。通過React Class的方式實現組件復用的具體步驟如下。
(1)定義一個HOC。

這里我們定義了一個HOC——withCounter,它用于獲取組件的業務數據。
(2)編寫一個展示性的普通組件。

這里我們編寫了一個普通組件,該組件作為展示性組件,獲取從HOC中傳遞過來的自定義userName屬性,然后將該屬性作為展示數據進行處理。
以上模式看上去挺不錯,而且有很多庫運用了這種模式,但我們仔細觀察會發現,它會增加代碼的層級關系,而且這種層級嵌套的方式可讀性不好。如果組件非常復雜,那么整個組件的可讀性就會非常差,比如aCom(bCom(cCom)),你甚至不知道里面到底發生了什么。
使用React Hooks方式實現組件復用的具體步驟如下。
(1)自定義Hooks鉤子。

(2)使用鉤子。

上述代碼通過useEffect鉤子獲取數據,因為React Hooks是函數模式,所以這里直接返回外部需要的數據,最終在外部組件中導入這些數據并展示。
由以上可知,使用React Hooks的方式比使用React Class的方式更容易理解,而且更重要的是React Hooks具有很好的可復用性,因為React Hooks是函數的形式,其他地方需要使用直接導入即可。
4.與TypeScript結合更簡單
我們幾乎不用關注React Hooks組件與TypeScript的具體結合方法,這是Class組件不具備的。下面我們使用TypeScript語言來編寫一個簡單的用戶信息展示組件UserInfo。
React Class+TypeScript版本如下。


React Hooks+TypeScript版本如下。

從上面的代碼可以更加直觀地看出,比起Class語法this指向的迷惑性,Hooks的函數式語法讓代碼看起來更為直觀與簡潔,各段業務邏輯天然隔離,這讓代碼維護性變得更好。
5.總結
最后,總結一下React Hooks的幾個優點,具體如下。
□聲明一個簡單的組件只要幾行簡單的代碼。
□容易上手。對于初學者來說,相比復雜的Class的聲明周期,Hooks的鉤子函數更好理解。
□簡化業務。充分利用組件化的思想把業務拆分成多個組件,采用函數式編程風格、函數式組件,狀態保存在運行環境中,每個功能都包裹在函數中,整體風格更清爽、更優雅。
□方便數據管理。相當于兩種提升:各個組件不用使用非常復雜的props多層傳輸就實現了解耦;向prop或狀態取值更加方便,函數組件都從當前作用域直接獲取變量,而Class組件需要先訪問實例this,再訪問其屬性或者方法。
□便于重構。因為減少了很多模板代碼,組件,特別是小組件寫起來更加省事。人們更愿意去拆分組件。而組件粒度越細,被復用的可能性越大。這樣,Hooks也在不知不覺中改變人們的開發習慣,提高項目的組件復用率。
1.1.2 React Hooks的缺點
任何產品都不可能是完美的,React Hooks相比React Class也有自己的不足之處。
1.狀態不同步問題
React Hooks是函數的形式,而函數的運行是獨立的,每個函數都有一個獨立的作用域。函數的變量保存在運行時的作用域里,當我們有異步操作的時候,經常會碰到異步回調的變量引用的是之前的,值沒有更新。這其實就是我們常說的閉包問題。
下面看一個具體的示例。

上述代碼首先聲明了number變量,用來保存按鈕點擊的次數,然后設置了兩個按鈕:一個按鈕立即執行點擊,執行number++;而另一個按鈕延時執行點擊,延時兩秒后執行number++。
運行上述代碼,先點擊“延時執行”按鈕,然后點擊“立即執行”按鈕,最終輸出結果如圖1-2所示。輸出的值并不是我們想要的。

圖1-2 狀態不同步示例
本來我們希望控制臺輸出的結果為10,但控制臺真正輸出的值卻是舊值0。如果使用React Class實現這個功能,顯然輸出的值是我們希望的10。
造成這種結果的原因是,函數的變量保存在運行時的作用域里,在點擊“延時執行”按鈕的時候,按鈕內執行函數的內部作用域將變量number復制了一份,因此它的值就還是之前的舊值0。這就是React Hooks狀態不同步的問題。
當然,這個問題能夠通過useRef鉤子來解決。useRef將在后文中專門介紹,這里暫不展開。
2.useEffect依賴問題
有時候,某個useEffect依賴某個函數的不可變性(不會被改變的變量),這個函數的不可變性又依賴另一個函數的不可變性,這樣便形成了一條依賴鏈。一旦這條依賴鏈的某個節點被意外改變,那么所有useEffect就會被意外觸發。如果某個組件中依賴層級很多,那么就會造成嚴重的性能問題。
解決這個問題的方案就是,盡量減少useEffect的依賴項,讓useEffect的每個依賴項都是明確的,保持它的單一職責性,增強組件的顆粒化。
下面通過實現一個常見的列表頁面來展示useEffect依賴項顆粒化的必要性。
不推薦如下做法。


在上述代碼中,頁面根據location頁面路由的變化更新頁面的導航,然后監聽頁面輸入框中的內容和分頁table的頁碼,從而調用對應的更新函數。
推薦的做法如下。


在上述代碼中,把useEffect依賴項根據業務類型進行了拆分,這樣不僅可以確保它們僅用于一種效果,從而防止發生意外的錯誤,而且可以讓代碼更加直觀和更好維護。
如果大家對useEffect的使用還有疑惑,可以暫時跳過此部分,后文有對useEffect更加具體的介紹。
1.1.3 使用React Hooks時的注意事項
要想用好React Hooks,需要注意以下事項。
1.自定義的React Hooks要遵循一些命名規范
自定義React Hooks的命名一律使用use作為前綴,形如useXXX,例如userUserInfo。雖然這僅是一種編碼習慣,但是為了代碼的可維護性,我們必須遵守這個規范。
2.僅在最外層調用React Hooks
不要在循環、條件和嵌套函數內調用React Hooks。當你想有條件地使用某些React Hooks時,請在這些Hooks中寫入條件。這樣能夠確保每次組件呈現時都以相同的順序調用React Hooks,避免出現一些不可確定的bug。
為了幫助大家理解,下面使用React Hooks實現一個根據登錄信息設置登錄狀態的組件。
不推薦如下寫法。


上述代碼中,組件會根據userName中是否存在值,來在localStorage中設置一個狀態,用于區分是否已經登錄。這里直接在函數體內根據userName的值判斷是否執行useEffect鉤子函數,會導致useEffect鉤子每次的執行與否都是根據userName的值來決定的,從而使得每次當前組件的渲染順序是不確定的,這樣,如果存在其他的業務邏輯,就很容易出現bug。
推薦的寫法如下。

3.僅從React函數中調用React Hooks
不要從常規JavaScript函數中調用React Hooks,只在自定義React Hooks或者React組件中調用React Hooks,這不僅能夠確保組件中的所有狀態邏輯都清晰可見,還能夠避免出現一些不可確定的bug。
- 一線架構師實踐指南
- Knative最佳實踐
- 中文版Revit 2018基礎培訓教程(全彩版)
- 軟件需求分析實戰
- 卡爾曼濾波原理及應用:MATLAB仿真(第2版)
- Unity手機游戲開發:從搭建到發布上線全流程實戰
- CATIA V5 從入門到精通(第二版)
- 全棧Monorepo開發實戰(Vue 3+Fastify+Deno+pnpm)
- Unity 2017經典游戲開發教程:算法分析與實現
- 持續交付2.0:業務引領的DevOps精要(增訂本)
- Bootstrap實戰
- Serverless核心技術和大規模實踐
- Spring Boot趣味實戰課
- Vue.js 3企業級項目開發實戰(微課視頻版)
- 看透Spring MVC:源代碼分析與實踐