useEvent顯著降低Hooks負擔的原生Hook
前言
推薦閱讀:# useMemo..一把梭?達咩!?|一文告訴你為什么React不把他們設(shè)為默認方法 #useEvent 是一個剛剛提案的原生Hook,還處于RFC。討論地址在這里~下面有些代碼就是來自其中
RFC:Request for Comments 提案還在廣泛的討論階段
沒有 useEvent 的時候??
我們先看看不用 useEvent 的情況:
function Chat() {
const [text, setText] = useState('');
// ?? Always a different function
const onClick = () => {
sendMessage(text);
};
return <SendButton onClick={onClick} />;
}
其中點擊事件的回調(diào)函數(shù) onClick 中需要讀取當前鍵入的文本text,這里的onClick隨著組件重新渲染一次次地重新創(chuàng)建,每次都會是不一樣的引用,這顯然帶來了性能損耗,如果你想對其進行優(yōu)化,你可能會這樣做:
function Chat() {
const [text, setText] = useState('');
// ?? A different function whenever `text` changes
const onClick = useCallback(() => {
sendMessage(text);
}, [text]);
return <SendButton onClick={onClick} />;
}
通過 useCallback 返回一個 memoized 回調(diào)函數(shù)。
useCallback: 返回一個 memoized 回調(diào)函數(shù)。 把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項數(shù)組作為參數(shù)傳入 useCallback,它將返回該回調(diào)函數(shù)的 memoized 版本,該回調(diào)函數(shù)僅在某個依賴項改變時才會更新。當你把回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子組件時,它將非常有用。 useCallback(fn, deps) 相當于 useMemo(() => fn, deps)。
最終使得onClick的引用始終不變但是!
onClcik這個方法有需要保證每次都要拿到最新的、正確的text,所以他的deps中就自然是設(shè)置了text—— 壞了,“又回到最初的起點~”。隨著每一次keystroke,onClick又變成了上面的情況:
Always a different function
但你又不能將其從deps中移除,移除了他就只能拿到text的初始值,失去了他本該有的功能...
小 useEvent 來給他整個活??
useEvent就是為了解決此類問題,所以他干脆不要deps了,他就是一直返回一個相同的函數(shù)引用,哪怕text發(fā)生變化。當然,保證它也能拿到最新的、正確的**text**。
function Chat() {
const [text, setText] = useState('');
// ? Always the same function (even if `text` changes)
const onClick = useEvent(() => {
sendMessage(text);
});
return <SendButton onClick={onClick} />;
}
現(xiàn)在好了:
- onClick 的引用始終是同一個
- 保證每次都能拿到最新的、正確的
text
當然還有其他一些場景,但是大致需求原理相同,就是不想讓A因為b變化而總是重新加載,但是又因為要拿到b恰當?shù)闹担?code>deps中必須b,導(dǎo)致不得不重新加載,掉進了“圈圈圓圓圈圈~”的陷阱。更多場景這里就不再贅述。更多案例可查看文末的學習資源~
總而言之,用useEvent給他裹上就是香,就是可以同時達到上面兩個效果:
- 引用不變
- 拿到恰當?shù)闹?/li>
這是咋做到的??
說了這么多,我們來看看他這是咋做到的
他大概是這么個形狀:(不是源碼就長這樣的意思嗷)
// (!) Approximate behavior
function useEvent(handler) {
const handlerRef = useRef(null);
// In a real implementation, this would run before layout effects
useLayoutEffect(() => {
handlerRef.current = handler;
});
return useCallback((...args) => {
// In a real implementation, this would throw if called during render
const fn = handlerRef.current;
return fn(...args);
}, []);
}
先回顧幾個Hook相關(guān)知識點:
useRef
useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。返回的 ref 對象在組件的整個生命周期內(nèi)持續(xù)存在。
這里通過 useRef 保存回調(diào)函數(shù)handler到handlerRef.current,然后再在 useCallback 中從handlerRef.current來取函數(shù)再調(diào)用,這樣避免了直接調(diào)用,跳出了閉包陷阱。并且不出意外的話handler在整個生命周期內(nèi)持續(xù)存在,也就是只有一個引用。
useLayoutEffect
這個 useLayoutEffect 可能沒那么常用,我們來看看這是啥嘞
其函數(shù)簽名與 useEffect 相同,但它會在所有的 DOM 變更之后同步調(diào)用 effect??梢允褂盟鼇碜x取 DOM 布局并同步觸發(fā)重渲染。在瀏覽器執(zhí)行繪制之前,useLayoutEffect 內(nèi)部的更新計劃將被同步刷新。
useEffect
回顧一下 useEffect
默認情況下,effect 將在每輪渲染結(jié)束后執(zhí)行
兩者的區(qū)別
好了,現(xiàn)在我給你用一個字總結(jié)一下兩者區(qū)別,useLayoutEffect 更“快”!這個“塊”不是速度更快,而是他“搶跑”了哩。useLayoutEffect 是在render之前同步執(zhí)行,useEffect在render之后異步執(zhí)行,這里就是保證useLayoutEffect 里的回調(diào)肯定比useEffect更早前被調(diào)用、被執(zhí)行。
useCallback執(zhí)行時機
前面說到
useCallback(fn, deps) 相當于 useMemo(() => fn, deps)。
文檔里是這樣說 useMemo 的:
記住,傳入 useMemo 的函數(shù)會在渲染期間執(zhí)行。請不要在這個函數(shù)內(nèi)部執(zhí)行與渲染無關(guān)的操作,諸如副作用這類的操作屬于 useEffect 的適用范疇,而不是 useMemo。
也就是他是在render時執(zhí)行的,也就是保證了賦值handler給handlerRef.current是在前面發(fā)生
這里的作用
這里返回的是一個useCallback包裹后 memoized函數(shù),其中從handlerRef.current中獲取函數(shù),并且deps為[],也就是說他不會再次更新。
捋一捋??
回顧完知識點我們也就濾清了這個useEvent方法,一句話總結(jié)就是:它接收一個回調(diào)函數(shù)handler作為參數(shù),提供給你一個穩(wěn)定的函數(shù)(始終只有一個引用)并且調(diào)用時都是用的你傳入的最新的參數(shù)...args——比如前面案例中的text,始終都是最新的、正確的、恰當?shù)摹?/strong>再結(jié)合一開始的案例,大概流程就是這樣:

不過目前這個也是 5.4號剛剛提出,也就是昨天,在正式生產(chǎn)環(huán)境中使用應(yīng)該還早,但我希望你還是能從本文有所收獲滴~
以上就是useEvent顯著降低Hooks負擔的原生Hook的詳細內(nèi)容,更多關(guān)于useEvent降低Hooks負擔原生Hook的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Remix集成antd和pro-components的過程示例
這篇文章主要為大家介紹了Remix集成antd和pro-components的過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
解決React報錯useNavigate()?may?be?used?only?in?context?of
這篇文章主要為大家介紹了解決React報錯useNavigate()?may?be?used?only?in?context?of?Router,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12
React+Spring實現(xiàn)跨域問題的完美解決方法
這篇文章主要介紹了React+Spring實現(xiàn)跨域問題的完美解決方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08
react使用節(jié)流函數(shù)防止重復(fù)點擊問題
這篇文章主要介紹了react使用節(jié)流函數(shù)防止重復(fù)點擊問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06
flouting?ui定位組件完美替代ant?deisgn使用詳解
這篇文章主要為大家介紹了flouting?ui定位組件完美替代ant?deisgn使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
React18使用Echarts和MUI實現(xiàn)一個交互性的溫度計
這篇文章我們將結(jié)合使用React 18、Echarts和MUI(Material-UI)庫,展示如何實現(xiàn)一個交互性的溫度計,感興趣的小伙伴可以跟隨小編一起學習一下2024-01-01
React??memo允許你的組件在?props?沒有改變的情況下跳過重新渲染的問題記錄
使用?memo?將組件包裝起來,以獲得該組件的一個?記憶化?版本,只要該組件的?props?沒有改變,這個記憶化版本就不會在其父組件重新渲染時重新渲染,這篇文章主要介紹了React??memo允許你的組件在?props?沒有改變的情況下跳過重新渲染,需要的朋友可以參考下2024-06-06
詳解React開發(fā)中使用require.ensure()按需加載ES6組件
本篇文章主要介紹了詳解React開發(fā)中使用require.ensure()按需加載ES6組件,非常具有實用價值,需要的朋友可以參考下2017-05-05

