useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook
前言
推薦閱讀:# useMemo..一把梭?達(dá)咩!?|一文告訴你為什么React不把他們?cè)O(shè)為默認(rèn)方法 #useEvent 是一個(gè)剛剛提案的原生Hook,還處于RFC。討論地址在這里~下面有些代碼就是來(lái)自其中
RFC:Request for Comments 提案還在廣泛的討論階段
沒(méi)有 useEvent 的時(shí)候??
我們先看看不用 useEvent 的情況:
function Chat() { const [text, setText] = useState(''); // ?? Always a different function const onClick = () => { sendMessage(text); }; return <SendButton onClick={onClick} />; }
其中點(diǎn)擊事件的回調(diào)函數(shù) onClick
中需要讀取當(dāng)前鍵入的文本text
,這里的onClick
隨著組件重新渲染一次次地重新創(chuàng)建,每次都會(huì)是不一樣的引用,這顯然帶來(lái)了性能損耗,如果你想對(duì)其進(jìn)行優(yōu)化,你可能會(huì)這樣做:
function Chat() { const [text, setText] = useState(''); // ?? A different function whenever `text` changes const onClick = useCallback(() => { sendMessage(text); }, [text]); return <SendButton onClick={onClick} />; }
通過(guò) useCallback 返回一個(gè) memoized 回調(diào)函數(shù)。
useCallback: 返回一個(gè) memoized 回調(diào)函數(shù)。 把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項(xiàng)數(shù)組作為參數(shù)傳入 useCallback
,它將返回該回調(diào)函數(shù)的 memoized
版本,該回調(diào)函數(shù)僅在某個(gè)依賴項(xiàng)改變時(shí)才會(huì)更新。當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過(guò)優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子組件時(shí),它將非常有用。 useCallback(fn, deps)
相當(dāng)于 useMemo(() => fn, deps)
。
最終使得onClick
的引用始終不變但是!
onClcik
這個(gè)方法有需要保證每次都要拿到最新的、正確的text,所以他的deps
中就自然是設(shè)置了text
—— 壞了,“又回到最初的起點(diǎn)~”。隨著每一次keystroke
,onClick
又變成了上面的情況:
Always a different function
但你又不能將其從deps
中移除,移除了他就只能拿到text
的初始值,失去了他本該有的功能...
小 useEvent 來(lái)給他整個(gè)活??
useEvent
就是為了解決此類問(wèn)題,所以他干脆不要deps
了,他就是一直返回一個(gè)相同的函數(shù)引用,哪怕text
發(fā)生變化。當(dāng)然,保證它也能拿到最新的、正確的**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 的引用始終是同一個(gè)
- 保證每次都能拿到最新的、正確的
text
當(dāng)然還有其他一些場(chǎng)景,但是大致需求原理相同,就是不想讓A
因?yàn)?code>b變化而總是重新加載,但是又因?yàn)橐玫?code>b恰當(dāng)?shù)闹?,所?code>deps中必須b
,導(dǎo)致不得不重新加載,掉進(jìn)了“圈圈圓圓圈圈~”的陷阱。更多場(chǎng)景這里就不再贅述。更多案例可查看文末的學(xué)習(xí)資源~
總而言之,用useEvent
給他裹上就是香,就是可以同時(shí)達(dá)到上面兩個(gè)效果:
- 引用不變
- 拿到恰當(dāng)?shù)闹?/li>
這是咋做到的??
說(shuō)了這么多,我們來(lái)看看他這是咋做到的
他大概是這么個(gè)形狀:(不是源碼就長(zhǎng)這樣的意思嗷)
// (!) 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); }, []); }
先回顧幾個(gè)Hook相關(guān)知識(shí)點(diǎn):
useRef
useRef 返回一個(gè)可變的 ref 對(duì)象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。返回的 ref 對(duì)象在組件的整個(gè)生命周期內(nèi)持續(xù)存在。
這里通過(guò) useRef 保存回調(diào)函數(shù)handler
到handlerRef.current
,然后再在 useCallback 中從handlerRef.current
來(lái)取函數(shù)再調(diào)用,這樣避免了直接調(diào)用,跳出了閉包陷阱。并且不出意外的話handler
在整個(gè)生命周期內(nèi)持續(xù)存在,也就是只有一個(gè)引用。
useLayoutEffect
這個(gè) useLayoutEffect 可能沒(méi)那么常用,我們來(lái)看看這是啥嘞
其函數(shù)簽名與 useEffect 相同,但它會(huì)在所有的 DOM 變更之后同步調(diào)用 effect??梢允褂盟鼇?lái)讀取 DOM 布局并同步觸發(fā)重渲染。在瀏覽器執(zhí)行繪制之前,useLayoutEffect 內(nèi)部的更新計(jì)劃將被同步刷新。
useEffect
回顧一下 useEffect
默認(rèn)情況下,effect 將在每輪渲染結(jié)束后執(zhí)行
兩者的區(qū)別
好了,現(xiàn)在我給你用一個(gè)字總結(jié)一下兩者區(qū)別,useLayoutEffect
更“快”!這個(gè)“塊”不是速度更快,而是他“搶跑”了哩。useLayoutEffect
是在render
之前同步執(zhí)行,useEffect
在render
之后異步執(zhí)行,這里就是保證useLayoutEffect
里的回調(diào)肯定比useEffect
更早前被調(diào)用、被執(zhí)行。
useCallback執(zhí)行時(shí)機(jī)
前面說(shuō)到
useCallback(fn, deps)
相當(dāng)于 useMemo(() => fn, deps)
。
文檔里是這樣說(shuō) useMemo 的:
記住,傳入 useMemo 的函數(shù)會(huì)在渲染期間執(zhí)行。請(qǐng)不要在這個(gè)函數(shù)內(nèi)部執(zhí)行與渲染無(wú)關(guān)的操作,諸如副作用這類的操作屬于 useEffect 的適用范疇,而不是 useMemo。
也就是他是在render
時(shí)執(zhí)行的,也就是保證了賦值handler
給handlerRef.current
是在前面發(fā)生
這里的作用
這里返回的是一個(gè)useCallback
包裹后 memoized
函數(shù),其中從handlerRef.current
中獲取函數(shù),并且deps
為[]
,也就是說(shuō)他不會(huì)再次更新。
捋一捋??
回顧完知識(shí)點(diǎn)我們也就濾清了這個(gè)useEvent
方法,一句話總結(jié)就是:它接收一個(gè)回調(diào)函數(shù)handler
作為參數(shù),提供給你一個(gè)穩(wěn)定的函數(shù)(始終只有一個(gè)引用)并且調(diào)用時(shí)都是用的你傳入的最新的參數(shù)...args
——比如前面案例中的text
,始終都是最新的、正確的、恰當(dāng)?shù)摹?/strong>再結(jié)合一開(kāi)始的案例,大概流程就是這樣:
不過(guò)目前這個(gè)也是 5.4號(hào)剛剛提出,也就是昨天,在正式生產(chǎn)環(huán)境中使用應(yīng)該還早,但我希望你還是能從本文有所收獲滴~
以上就是useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook的詳細(xì)內(nèi)容,更多關(guān)于useEvent降低Hooks負(fù)擔(dān)原生Hook的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
新建的React Native就遇到vscode報(bào)警解除方法
這篇文章主要為大家介紹了新建的React Native就遇到vscode報(bào)警解除方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式
這篇文章主要介紹了React配置Redux并結(jié)合本地存儲(chǔ)設(shè)置token方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01React通過(guò)redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例
這篇文章主要介紹了React通過(guò)redux-persist持久化數(shù)據(jù)存儲(chǔ)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02ReactNative-JS 調(diào)用原生方法實(shí)例代碼
這篇文章主要介紹了ReactNative-JS 調(diào)用原生方法實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10react axios配置代理(proxy),如何解決本地開(kāi)發(fā)時(shí)的跨域問(wèn)題
這篇文章主要介紹了react axios配置代理(proxy),如何解決本地開(kāi)發(fā)時(shí)的跨域問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07詳解React項(xiàng)目如何修改打包地址(編譯輸出文件地址)
這篇文章主要介紹了詳解React項(xiàng)目如何修改打包地址(編譯輸出文件地址),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03React中useEffect與生命周期鉤子函數(shù)的對(duì)應(yīng)關(guān)系說(shuō)明
這篇文章主要介紹了React中useEffect與生命周期鉤子函數(shù)的對(duì)應(yīng)關(guān)系說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09React倒計(jì)時(shí)功能實(shí)現(xiàn)代碼——解耦通用
這篇文章主要介紹了React倒計(jì)時(shí)功能實(shí)現(xiàn)——解耦通用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09