前言
React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看來器贩,使用 React Hooks 相比于從前的類組件有以下幾點好處:
代碼可讀性更強,原本同一塊功能的代碼邏輯被拆分在了不同的生命周期函數(shù)中衔统,容易使開發(fā)者不利于維護和迭代提针,通過 React Hooks 可以將功能代碼聚合,方便閱讀維護施敢;
組件樹層級變淺周荐,在原本的代碼中,我們經(jīng)常使用 HOC/render props 等方式來復用組件的狀態(tài)僵娃,增強功能等概作,無疑增加了組件樹層數(shù)及渲染,而在 React Hooks 中默怨,這些功能都可以通過強大的自定義的 Hooks 來實現(xiàn)讯榕;
關于這方面的文章,我們根據(jù)使用場景分別進行舉例說明匙睹,幫助你認識理解并可以熟練運用 React Hooks 大部分特性愚屁。
辛苦整理良久,還望手動點贊鼓勵~
博客 github地址為:github.com/fengshi123/… 痕檬,匯總了作者的所有博客集绰,歡迎關注及 star ~
一、State Hook
1谆棺、基礎用法
2栽燕、更新
更新分為以下兩種方式,即直接更新和函數(shù)式更新改淑,其應用場景的區(qū)分點在于:
直接更新不依賴于舊 state 的值碍岔;
函數(shù)式更新依賴于舊 state 的值;
3朵夏、實現(xiàn)合并
與 class 組件中的 setState 方法不同蔼啦,useState 不會自動合并更新對象,而是直接替換它仰猖。我們可以用函數(shù)式的 setState 結合展開運算符來達到合并更新對象的效果捏肢。
4奈籽、惰性初始化 state
initialState 參數(shù)只會在組件的初始渲染中起作用,后續(xù)渲染時會被忽略鸵赫。其應用場景在于:創(chuàng)建初始 state 很昂貴時衣屏,例如需要通過復雜計算獲得;那么則可以傳入一個函數(shù)辩棒,在函數(shù)中計算并返回初始的 state狼忱,此函數(shù)只在初始渲染時被調(diào)用:
5、一些重點
(1)不像 class 中的 this.setState 一睁,Hook 更新 state 變量總是替換它而不是合并它钻弄; (2)推薦使用多個 state 變量,而不是單個 state 變量者吁,因為 state 的替換邏輯而不是合并邏輯窘俺,并且利于后續(xù)的相關 state 邏輯抽離; (3)調(diào)用 State Hook 的更新函數(shù)并傳入當前的 state 時复凳,React 將跳過子組件的渲染及 effect 的執(zhí)行批销。(React 使用 Object.is 比較算法 來比較 state。)
二染坯、Effect Hook
1均芽、基礎用法
2、清除操作
為防止內(nèi)存泄漏单鹿,清除函數(shù)會在組件卸載前執(zhí)行掀宋;如果組件多次渲染(通常如此),則在執(zhí)行下一個 effect 之前仲锄,上一個 effect 就已被清除劲妙,即先執(zhí)行上一個 effect 中 return 的函數(shù),然后再執(zhí)行本 effect 中非 return 的函數(shù)儒喊。
3镣奋、執(zhí)行時期
與 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 調(diào)度的 effect 不會阻塞瀏覽器更新屏幕怀愧,這讓你的應用看起來響應更快侨颈;(componentDidMount 或 componentDidUpdate 會阻塞瀏覽器更新屏幕)
4、性能優(yōu)化
默認情況下芯义,React 會每次等待瀏覽器完成畫面渲染之后延遲調(diào)用 effect哈垢;但是如果某些特定值在兩次重渲染之間沒有發(fā)生變化,你可以通知 React 跳過對 effect 的調(diào)用扛拨,只要傳遞數(shù)組作為 useEffect 的第二個可選參數(shù)即可:如下所示耘分,如果 count 值兩次渲染之間沒有發(fā)生變化,那么第二次渲染后就會跳過 effect 的調(diào)用;
5求泰、模擬 componentDidMount
如果想只運行一次的 effect(僅在組件掛載和卸載時執(zhí)行)央渣,可以傳遞一個空數(shù)組([ ])作為第二個參數(shù),如下所示渴频,原理跟第 4 點性能優(yōu)化講述的一樣芽丹;
6、最佳實踐
要記住 effect 外部的函數(shù)使用了哪些 props 和 state 很難枉氮,這也是為什么 通常你會想要在 effect 內(nèi)部 去聲明它所需要的函數(shù)。
如果出于某些原因你無法把一個函數(shù)移動到 effect 內(nèi)部暖庄,還有一些其他辦法:
你可以嘗試把那個函數(shù)移動到你的組件之外聊替。那樣一來,這個函數(shù)就肯定不會依賴任何 props 或 state培廓,并且也不用出現(xiàn)在依賴列表中了惹悄;
萬不得已的情況下,你可以 把函數(shù)加入 effect 的依賴但 把它的定義包裹 進 useCallback Hook肩钠。這就確保了它不隨渲染而改變泣港,除非它自身的依賴發(fā)生了改變;
推薦啟用 eslint-plugin-react-hooks 中的 exhaustive-deps 規(guī)則价匠,此規(guī)則會在添加錯誤依賴時發(fā)出警告并給出修復建議 当纱;
7、一些重點
(1)可以把 useEffect Hook 看做 componentDidMount踩窖,componentDidUpdate和 componentWillUnmount這三個函數(shù)的組合坡氯; (2)在 React 的 class 組件中,render 函數(shù)是不應該有任何副作用的洋腮; 一般來說箫柳,在這里執(zhí)行操作太早了,我們基本上都希望在 React 更新 DOM 之后才執(zhí)行我們的操作啥供。
三悯恍、useContext
用來處理多層級傳遞數(shù)據(jù)的方式,在以前組件樹中伙狐,跨層級祖先組件想要給孫子組件傳遞數(shù)據(jù)的時候涮毫,除了一層層 props 往下透傳之外,我們還可以使用 React Context API 來幫我們做這件事贷屎。使用例子如下所示 (1)使用 React Context API窒百,在組件外部建立一個 Context
(2)使用 Context.Provider提供了一個 Context 對象,這個對象可以被子組件共享
(3)useContext()鉤子函數(shù)用來引入 Context 對象豫尽,并且獲取到它的值
四篙梢、useReducer
1、基礎用法
比 useState 更適用的場景:例如 state 邏輯處理較復雜且包含多個子值美旧,或者下一個 state 依賴于之前的 state 等渤滞;例子如下所示
2贬墩、惰性初始化 state
五、Memo
如下所示妄呕,當父組件重新渲染時陶舞,子組件也會重新渲染,即使子組件的 props 和 state 都沒有改變
改進:我們可以使用 memo 包一層绪励,就能解決上面的問題肿孵;但是僅僅解決父組件沒有傳參給子組件的情況以及父組件傳簡單類型的參數(shù)給子組件的情況(例如 string、number疏魏、boolean等)停做;如果有傳復雜屬性應該使用 useCallback(回調(diào)事件)或者 useMemo(復雜屬性)
六、useMemo
假設以下場景大莫,父組件在調(diào)用子組件時傳遞 info 對象屬性蛉腌,點擊父組件按鈕時,發(fā)現(xiàn)控制臺會打印出子組件被渲染的信息只厘。
分析原因:
點擊父組件按鈕烙丛,觸發(fā)父組件重新渲染;
父組件渲染羔味,const info = { name, age } 一行會重新生成一個新對象河咽,導致傳遞給子組件的 info 屬性值變化,進而導致子組件重新渲染赋元。
解決:
使用 useMemo 將對象屬性包一層库北,useMemo 有兩個參數(shù):
第一個參數(shù)是個函數(shù),返回的對象指向同一個引用们陆,不會創(chuàng)建新對象寒瓦;
第二個參數(shù)是個數(shù)組,只有數(shù)組中的變量改變時坪仇,第一個參數(shù)的函數(shù)才會返回一個新的對象杂腰。
七 、useCallback
接著第六章節(jié)的例子椅文,假設需要將事件傳給子組件喂很,如下所示,當點擊父組件按鈕時皆刺,發(fā)現(xiàn)控制臺會打印出子組件被渲染的信息少辣,說明子組件又被重新渲染了。
分析下原因:
點擊父組件按鈕羡蛾,改變了父組件中 count 變量值(父組件的 state 值)漓帅,進而導致父組件重新渲染;
父組件重新渲染時,會重新創(chuàng)建 changeName 函數(shù)忙干,即傳給子組件的 changeName 屬性發(fā)生了變化器予,導致子組件渲染;
解決:?修改父組件的 changeName 方法捐迫,用 useCallback 鉤子函數(shù)包裹一層乾翔, useCallback 參數(shù)與 useMemo 類似
八、useRef
以下分別介紹 useRef 的兩個使用場景:
1施戴、指向 dom 元素
如下所示反浓,使用 useRef 創(chuàng)建的變量指向一個 input 元素,并在頁面渲染后使 input 聚焦
2赞哗、存放變量
useRef 在 react hook 中的作用, 正如官網(wǎng)說的, 它像一個變量, 類似于 this , 它就像一個盒子, 你可以存放任何東西. createRef 每次渲染都會返回一個新的引用雷则,而 useRef 每次都會返回相同的引用,如下例子所示:
九懈玻、useImperativeHandle
使用場景:通過 ref 獲取到的是整個 dom 節(jié)點巧婶,通過 useImperativeHandle 可以控制只暴露一部分方法和屬性乾颁,而不是整個 dom 節(jié)點涂乌。
十、useLayoutEffect
其函數(shù)簽名與 useEffect 相同英岭,但它會在所有的 DOM 變更之后同步調(diào)用 effect湾盒,這里不再舉例。
useLayoutEffect 和平常寫的 Class 組件的 componentDidMount 和 componentDidUpdate 同時執(zhí)行诅妹;
useEffect 會在本次更新完成后罚勾,也就是第 1 點的方法執(zhí)行完成后,再開啟一次任務調(diào)度吭狡,在下次任務調(diào)度中執(zhí)行 useEffect尖殃;
結尾 需要這些資料,可以 關注公眾號? 清風酔? 領取