react之自定義hooks
原文合集地址如下,有需要的朋友可以關(guān)注
本文地址
合集地址
任何相對(duì)獨(dú)立、復(fù)用性強(qiáng)的邏輯,都可以 extract 為自定義 Hook,自定義 Hook 是一種復(fù)用 React 的狀態(tài)邏輯的函數(shù)。 自定義 Hook 的主要特點(diǎn)是:
抽象組件間的狀態(tài)邏輯,方便復(fù)用
讓功能組件更純粹,更易于維護(hù)
自定義 Hook 可以調(diào)用其他 Hook
為什么要用自定義 Hook?
提煉能復(fù)用的邏輯 許多組件有相似的狀態(tài)邏輯,使用自定義 Hook 可以很方便地提取出來復(fù)用。
解決復(fù)雜組件的可讀性問題 使用自定義 Hook 將復(fù)雜組件拆分為更小的功能獨(dú)立的函數(shù),有助于提高代碼的可讀性。
管理數(shù)據(jù)更新 使用獨(dú)立的 Hook 函數(shù)來管理數(shù)據(jù)請(qǐng)求、處理異步邏輯、數(shù)據(jù)緩存等,易于維護(hù)。
分離狀態(tài)邏輯 自定義 Hook 讓函數(shù)組件更純粹,只負(fù)責(zé) UI,狀態(tài)邏輯則交給 Hook。
調(diào)用其他 Hook 自定義 Hook 本身還可以調(diào)用 useState、useEffect 等其他 React Hook。
以下是我總結(jié)的一些常用的hooks
1、useUpdateEffect
useUpdateEffect作用
useUpdateEffect
是一個(gè)自定義的 React Hook,用于在組件更新時(shí)執(zhí)行副作用操作。它類似于 React 的 useEffect
,但是會(huì)忽略組件的初始渲染階段,只在組件更新時(shí)執(zhí)行副作用操作。
在 React 中,useEffect
會(huì)在組件的每次渲染(包括初始渲染)完成后執(zhí)行副作用操作。但有時(shí)候我們只想在組件更新時(shí)執(zhí)行某些操作,而不關(guān)心初始渲染階段的操作。這就是 useUpdateEffect
的用途。
以下是一個(gè)示例:
import?{?useEffect,?useState?}?from?'react';
function?MyComponent()?{
??const?[count,?setCount]?=?useState(0);
??useEffect(()?=>?{
????console.log('useEffect?-?Component?has?rendered');
??});
??useUpdateEffect(()?=>?{
????console.log('useUpdateEffect?-?Component?has?updated');
??});
??return?(
????<div>
??????<p>Count:?{count}</p>
??????<button?onClick={()?=>?setCount(count?+?1)}>Increment</button>
????</div>
??);
}
在上述示例中,當(dāng)點(diǎn)擊 "Increment" 按鈕時(shí),count
的值會(huì)增加并觸發(fā)組件的重新渲染。useEffect
會(huì)在每次渲染后執(zhí)行,而 useUpdateEffect
只會(huì)在組件更新時(shí)執(zhí)行。
通過使用 useUpdateEffect
,你可以在組件更新時(shí)執(zhí)行一些特定的副作用操作,如請(qǐng)求數(shù)據(jù)、更新狀態(tài)等,而不需要關(guān)心初始渲染階段的操作。
為什么會(huì)需要用到useUpdateEffect
在某些情況下,我們希望在 React 組件更新時(shí)執(zhí)行一些特定的副作用操作,而不在初始渲染階段執(zhí)行這些操作。這種情況下,我們可以使用類似于 useUpdateEffect
的自定義 Hook。
以下是一些使用 useUpdateEffect
的常見情況:
避免初始渲染時(shí)執(zhí)行副作用:有些副作用操作可能只需要在組件更新時(shí)執(zhí)行,例如發(fā)送網(wǎng)絡(luò)請(qǐng)求、更新特定狀態(tài)等。使用
useUpdateEffect
可以確保這些副作用操作在初始渲染時(shí)被跳過,只在組件更新時(shí)執(zhí)行。監(jiān)聽特定狀態(tài)的變化:有時(shí)我們只關(guān)心特定狀態(tài)的變化,并希望在狀態(tài)發(fā)生變化時(shí)執(zhí)行相應(yīng)的操作。通過將狀態(tài)值作為
useUpdateEffect
的依賴項(xiàng),可以確保副作用操作只在這些狀態(tài)發(fā)生變化時(shí)觸發(fā)。更新外部資源或庫:有些第三方庫或外部資源可能需要在組件更新時(shí)進(jìn)行更新或重新初始化。使用
useUpdateEffect
可以確保在組件更新時(shí)調(diào)用相應(yīng)的函數(shù)或方法,以便正確地更新這些外部資源。
通過使用 useUpdateEffect
,我們可以更加精確地控制副作用操作的觸發(fā)時(shí)機(jī),避免不必要的重復(fù)執(zhí)行,以及在需要時(shí)處理特定的更新邏輯。
需要注意的是,React 自帶的 useEffect
可以處理大多數(shù)情況下的副作用操作,而 useUpdateEffect
是在某些特定場(chǎng)景下的補(bǔ)充工具。在大多數(shù)情況下,使用 useEffect
即可滿足需求。
自定義useUpdateEffect
要自定義一個(gè)類似于 useUpdateEffect
的自定義 Hook,你可以借助 React 的 useEffect
和 useRef
Hooks 來實(shí)現(xiàn)。以下是一個(gè)示例代碼:
import?{?useEffect,?useRef?}?from?'react';
function?useUpdateEffect(effect,?dependencies)?{
??const?isMounted?=?useRef(false);
??useEffect(()?=>?{
????if?(isMounted.current)?{
??????effect();
????}?else?{
??????isMounted.current?=?true;
????}
??},?dependencies);
}
//?使用示例
function?MyComponent()?{
??const?[count,?setCount]?=?useState(0);
??useUpdateEffect(()?=>?{
????console.log('Component?has?updated');
??},?[count]);
??return?(
????<div>
??????<p>Count:?{count}</p>
??????<button?onClick={()?=>?setCount(count?+?1)}>Increment</button>
????</div>
??);
}
在上述示例中,我們創(chuàng)建了一個(gè)名為 useUpdateEffect
的自定義 Hook。它接受兩個(gè)參數(shù):effect
和 dependencies
。在內(nèi)部,我們使用了 useRef
來創(chuàng)建一個(gè)標(biāo)記是否已經(jīng)完成初始渲染的變量 isMounted
。
在 useEffect
中,我們檢查 isMounted
的值。如果 isMounted
的值為 true
,則表示組件已經(jīng)完成了初始渲染,此時(shí)執(zhí)行傳入的 effect
函數(shù)。否則,將 isMounted
的值設(shè)置為 true
,表示組件已完成初始渲染。
在使用時(shí),你可以像使用 useEffect
一樣,傳入 effect
函數(shù)和依賴項(xiàng)數(shù)組 dependencies
,并且 effect
函數(shù)只會(huì)在組件更新時(shí)執(zhí)行。
2、useTitle
useTitle 是一個(gè)相對(duì)經(jīng)典的自定義 React Hook ,用來控制瀏覽器標(biāo)題:
定義useTitle
import?{?useState,?useEffect?}?from?'react';
function?useTitle(initialTitle)?{
??const?[title,?setTitle]?=?useState(initialTitle);
?
??useEffect(()?=>?{
????document.title?=?title;
??},?[title]);
??return?setTitle;
}
使用useTitle:
function?Page()?{
??const?setTitle?=?useTitle('Default?Title');
?
??return?(
????<Button?onClick={()?=>?setTitle('New?Title')}>
??????Click?me
????</Button>
??)
}
點(diǎn)擊按鈕后,瀏覽器標(biāo)題會(huì)變成"New Title"。 它的工作原理是:
保存標(biāo)題的 state ,并記錄修改 setTitle()
用 useEffect 監(jiān)測(cè) title 變化,設(shè)置 document.title 所以一旦我們調(diào)用 setTitle('New Title') 改變 state ,useEffect 就會(huì)執(zhí)行,設(shè)置新的瀏覽器標(biāo)題。 useTitle 的優(yōu)點(diǎn)是:
抽象出設(shè)置標(biāo)題的邏輯,任何組件都可以共享
讓組件更純粹,只需要調(diào)用 setTitle() 接口即可 我們甚至可以抽象為更通用的 Hook:
js
function?useDocumentTitle(title)?{
??useEffect(()?=>?{
????document.title?=?title;
??},?[title]);
}
function?Page()?{
??useDocumentTitle('Default?Title');
??//?...
}
通過自定義 Hook ,可以方便地在任何組件控制標(biāo)題。
3、useForceUpdate
定義useForceUpdate
import?{?useState?}?from?'react';
function?useForceUpdate()?{
??const?[value,?setValue]?=?useState(0);?
??
??return?()?=>?{
????setValue(value?=>?value?+?1);?
??};
}
useForceUpdate的使用
const?forceUpdate?=?useForceUpdate();
//?模擬更新組件
forceUpdate();
這個(gè) Hook 返回了一個(gè)更新函數(shù)。在調(diào)用這個(gè)函數(shù)時(shí),使用useState
強(qiáng)制組件重新渲染。
這是基于以下原理實(shí)現(xiàn)的:
useState()會(huì)觸發(fā)組件重新渲染
state變化后,組件函數(shù)會(huì)重新執(zhí)行 函數(shù)式組件只有 state 或 props 變化時(shí)才會(huì)更新。 使用此 Hook 我們可以主動(dòng)觸發(fā)組件更新。 比如在使用過時(shí)數(shù)據(jù)時(shí):
//?過時(shí)數(shù)據(jù)?
const?{?data?}?=?useSomeHook();
//?更新組件
const?forceUpdate?=?useForceUpdate();
setInterval(()?=>?{
??forceUpdate();
},?5000);
每5秒強(qiáng)制組件一次,保證拿到最新數(shù)據(jù)。
4、useDebounce
定義
const?useDebounce?=?(value,?delay)?=>?{
??const?[debouncedValue,?setDebouncedValue]?=?useState(value);
??useEffect(()?=>?{
????const?handler?=?setTimeout(()?=>?{
??????setDebouncedValue(value);???
????},?delay);
????return?()?=>?{
??????clearTimeout(handler);?
????};
??},?[]);?//?設(shè)為空數(shù)組[]
??useEffect(()?=>?{
????clearTimeout(handler);?????
????handler?=?setTimeout(()?=>?{
??????setDebouncedValue(value);
????},?delay);
??},?[value]);??//?只依賴?value
??return?debouncedValue;
};
使用
const?inputValue?=?useDebounced(searchTerm,?500);
這里 每當(dāng)searchTerm
變化時(shí),會(huì)設(shè)置一個(gè) 500ms 的定時(shí)器。只有500ms內(nèi)沒有再改變searchTerm
,才會(huì)更新debouncedValue
。
這實(shí)現(xiàn)了防抖功能:在一定時(shí)間內(nèi)停止觸發(fā), 只執(zhí)行最后的動(dòng)作。
5、useThrottle
定義
const?useThrottle?=?(value,?limit)?=>?{
??const?[throttledValue,?setThrottledValue]?=?useState(value);
??useEffect(()?=>?{
????const?handler?=?setTimeout(()?=>?{
??????setThrottledValue(value);
????},?limit);
????return?()?=>?{
??????clearTimeout(handler);
????};
??},?[]);?//?應(yīng)設(shè)為空數(shù)組[]??
??
??useEffect(()?=>?{
????clearTimeout(handler);
????handler?=?setTimeout(()?=>?{
??????setThrottledValue(value);????
????},?limit);
??},?[value,?limit]);?
??
??return?throttledValue;??
};
使用
const?throttledValue?=?useThrottle(inputValue,?1000);
這里 每次inputValue
變化時(shí),會(huì)開始一個(gè)計(jì)時(shí)器。1s后才會(huì)更新throttledValue
,實(shí)現(xiàn)了節(jié)流功能。
6、useInterval
定義
const?useInterval?=?(callback,?delay)?=>?{
??const?savedCallback?=?useRef();
??useEffect(()?=>?{
????savedCallback.current?=?callback;
??});?
??useEffect(()?=>?{
????function?tick()?{
??????savedCallback.current();??????????
????}
????if?(delay?!==?null)?{
??????let?id?=?setInterval(tick,?delay);????????
??????return?()?=>?clearInterval(id);
????}
??},?[delay]);
}
使用
useInterval(()?=>?{
??//?...
},?1000);?
這里每1000ms就會(huì)調(diào)用一次回調(diào)函數(shù),實(shí)現(xiàn)了定時(shí)執(zhí)行指定函數(shù)的功能。 有任何問題歡迎留言討論學(xué)習(xí)