本篇主要和大家溝通關(guān)于ahooks ,我們可以理解為加深對 React hooks 的了解。
我們先說下關(guān)于抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。
其實我們應該培養(yǎng)閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。
注:本系列對 ahooks 的源碼解析是基于v3.3.13。
現(xiàn)在就進入主題用ahooks 來封裝 React要注意的“時機”?
Function Component VS Class Component
很多時候代碼要講究先后順序,要掌握周期,且需知道在不同的階段執(zhí)行不同操作的代碼,比如需要掛載完成之后才去獲取 dom 的值,否則可能會獲取不到相應的值。
Class Component
使用過 React 的 Class Component 的同學,就會知道其組件生命周期會分成三個狀態(tài):
Mounting(掛載):已插入真實 DOM
Updating(更新):正在被重新渲染
Unmounting(卸載):已移出真實 DOM
簡單版如下所示:
其中每個狀態(tài)中還會按順序調(diào)用不同的方法,直接看下圖:
先要詳細了解,就去官網(wǎng)詳看。可以看到,會有非常多的生命周期方法,而且在不同的版本,生命周期方法還不同。
Function Component
沒有生命周期的概念—Function Component ,它是更徹底的狀態(tài)驅(qū)動,它只有一個狀態(tài),React 負責將狀態(tài)渲染到視圖中。
對于 Function Component 來說由狀態(tài)到頁面渲染只有三步:
輸入狀態(tài)(prop、state)
執(zhí)行組件的邏輯,并在useEffect/useLayoutEffect中訂閱副作用
輸出UI(Dom節(jié)點)
重點是第二步,React 通過 useEffect/useLayoutEffect 訂閱副作用。Class Component 中的生命周期都可以通過 useEffect/useLayoutEffect 來實現(xiàn)。它們兩個的功能非常相似,我們這里看下 useEffect。
使用 useEffect 相當于告訴 React 組件需要在渲染后執(zhí)行某些操作,React 將在執(zhí)行 DOM 更新之后調(diào)用它。React 保證了每次運行 useEffect 的時候,DOM 已經(jīng)更新完畢。這就實現(xiàn)了 Class Component 中的 Mounting(掛載階段)。
當狀態(tài)發(fā)生變化的時候,它能夠執(zhí)行對應的邏輯、更行狀態(tài)并將結(jié)果渲染到視圖中,這就完成了 Class Component 中的 Updating(更新階段)。
最后通過在 useEffect 中返回一個函數(shù),它便可以清理副作用。它的規(guī)則是:
首次渲染不會進行清理,會在下一次渲染,清除上一次的副作用。
卸載階段也會執(zhí)行清除操作。
通過返回一個函數(shù),我們就能實現(xiàn) Class Component 中的 Unmounting(卸載階段)。
基于 useEffect/useLayoutEffect,ahooks 做了一些封裝,能夠讓你更加清晰的知道你的代碼執(zhí)行時機。
LifeCycle - 生命周期
useMount
只在組件初始化時執(zhí)行的 Hook。 useEffect 依賴假如為空,只會在組件初始化的時候執(zhí)行。
// 省略部分代碼 const useMount = (fn: () => void) => { // 省略部分代碼 // 單純就在 useEffect 基礎(chǔ)上封裝了一層 useEffect(() => { fn?.(); }, []); }; export default useMount;
useUnmount
useUnmount,組件卸載(unmount)時執(zhí)行的 Hook。
useEffect 可以在組件渲染后實現(xiàn)各種不同的副作用。有些副作用可能需要清除,所以需要返回一個函數(shù),這個函數(shù)會在組件卸載的時候執(zhí)行。
const useUnmount = (fn: () => void) => { const fnRef = useLatest(fn); useEffect( // 在組件卸載(unmount)時執(zhí)行的 Hook。 // useEffect 的返回值中執(zhí)行函數(shù) () => () => { fnRef.current(); }, [], ); }; export default useUnmount;
useUnmountedRef
獲取當前組件是否已經(jīng)卸載的 Hook。
通過判斷有沒有執(zhí)行 useEffect 中的返回值判斷當前組件是否已經(jīng)卸載。
// 獲取當前組件是否已經(jīng)卸載的 Hook。 const useUnmountedRef = () => { const unmountedRef = useRef(false); useEffect(() => { unmountedRef.current = false; // 如果已經(jīng)卸載,則會執(zhí)行 return 中的邏輯 return () => { unmountedRef.current = true; }; }, []); return unmountedRef; }; export default useUnmountedRef;
Effect
這里只會講官方文檔Effect下面的幾個,有部分是定時器、防抖節(jié)流等,咱們后面的系列具體分析。
useUpdateEffect 和 useUpdateLayoutEffect
useUpdateEffect 和 useUpdateLayoutEffect 的用法跟 useEffect 和 useLayoutEffect 一樣,只是會忽略首次執(zhí)行,只在依賴更新時執(zhí)行。
實現(xiàn)思路:初始化一個標識符,剛開始為 false。當首次執(zhí)行完的時候,置為 true。只有標識符為 true 的時候,才執(zhí)行回調(diào)函數(shù)。
// 忽略首次執(zhí)行 export const createUpdateEffect: (hook: effectHookType) => effectHookType = (hook) => (effect, deps) => { const isMounted = useRef(false); // for react-refresh hook(() => { return () => { isMounted.current = false; }; }, []); hook(() => { // 首次執(zhí)行完時候,設(shè)置為 true,從而下次依賴更新的時候可以執(zhí)行邏輯 if (!isMounted.current) { isMounted.current = true; } else { return effect(); } }, deps); };
useDeepCompareEffect和useDeepCompareLayoutEffect
用法與 useEffect 一致,但 deps 通過 lodash isEqual 進行深比較。
通過 useRef 保存上一次的依賴的值,跟當前的依賴對比(使用 lodash 的 isEqual),并將對比結(jié)果作為 useEffect 的依賴項,從而決定回調(diào)函數(shù)是否執(zhí)行。
const depsEqual = (aDeps: DependencyList, bDeps: DependencyList = []) => { return isEqual(aDeps, bDeps); }; const useDeepCompareEffect = (effect: EffectCallback, deps: DependencyList) => { // 通過 useRef 保存上一次的依賴的值 const ref = useRef<DependencyList>(); const signalRef = useRef<number>(0); // 判斷最新的依賴和舊的區(qū)別 // 如果相等,則變更 signalRef.current,從而觸發(fā) useEffect 中的回調(diào) if (!depsEqual(deps, ref.current)) { ref.current = deps; signalRef.current += 1; } useEffect(effect, [signalRef.current]); };
useUpdate
useUpdate 會返回一個函數(shù),調(diào)用該函數(shù)會強制組件重新渲染。
返回的函數(shù)通過變更 useState 返回的 state,從而促使組件進行更新。
import { useCallback, useState } from 'react'; const useUpdate = () => { const [, setState] = useState({}); // 通過設(shè)置一個全新的狀態(tài),促使 function 組件更新 return useCallback(() => setState({}), []); }; export default useUpdate;
總結(jié)與思考
寫代碼要清楚知道,組件的生命周期是怎樣的,還有代碼的執(zhí)行順序、執(zhí)行的時機是怎樣的。
在 Function Component 中,使用 useEffect/useLayoutEffect 完成了 Class Components 生命周期的職責。ahooks 這就是的代碼執(zhí)行時機,要是添加hook,就可以讓代碼具有可讀性以及邏輯更加清晰。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/128256.html
起因 社會在不斷的向前,技術(shù)也在不斷的完善進步。從 React Hooks 正式發(fā)布到現(xiàn)在,越來越多的項目正在使用 Function Component 替代 Class Component,Hooks 這一新特性也逐漸被廣泛的使用。 這樣的解析是不是很熟悉,在日常中時常都有用到,但也有一個可以解決這樣重復的就是對數(shù)據(jù)請求的邏輯處理,對防抖節(jié)流的邏輯處理等。 另一方面,由于 Hoo...
陷進到處都是啊!本篇文章就說說Hooks使用時存在所謂的閉包陷阱,看看下面代碼: functionChat(){ const[text,setText]=useState(''); constonClick=useCallback(()=>{ sendMessage(text); },[]); return<SendButtononClick=...
這是講 ahooks 源碼的第一篇文章,簡要就是以下幾點: 加深對 React hooks 的理解。 學習如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。 培養(yǎng)閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。 注:本系列對 ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見詳情。 第一篇主要介紹 a...
想必大家都能看得懂的源碼 ahooks 整體架構(gòu)篇,且可以使用插件化機制優(yōu)雅的封裝你的請求hook,現(xiàn)在我們就探討下ahooks 是怎么解決 React 的閉包問題的?。 React 的閉包問題 先來看一個例子: importReact,{useState,useEffect}from"react"; exportdefault()=>{ const[c...
之所以講這篇文章主要是為了加深對 React hooks 的理解。 因此,先要學習如何抽象自定義 hooks。構(gòu)建屬于自己的 React hooks 工具庫。 且培養(yǎng)閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。 現(xiàn)在看下ahooks 是怎么封裝 cookie/localStorage/sessionStorage 的。 cookie ahooks 封裝了 useCookie...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28