您好,登錄后才能下訂單哦!
這篇文章主要講解了“useEvent降低Hooks負擔的原生Hook分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“useEvent降低Hooks負擔的原生Hook分析”吧!
我們先看看不用 useEvent 的情況:
function Chat() { const [text, setText] = useState(''); // ???? Always a different function const onClick = () => { sendMessage(text); }; return <SendButton onClick={onClick} />; }
其中點擊事件的回調函數 onClick
中需要讀取當前鍵入的文本text
,這里的onClick
隨著組件重新渲染一次次地重新創建,每次都會是不一樣的引用,這顯然帶來了性能損耗,如果你想對其進行優化,你可能會這樣做:
function Chat() { const [text, setText] = useState(''); // ???? A different function whenever `text` changes const onClick = useCallback(() => { sendMessage(text); }, [text]); return <SendButton onClick={onClick} />; }
useCallback: 返回一個 memoized 回調函數。 把內聯回調函數及依賴項數組作為參數傳入 useCallback
,它將返回該回調函數的 memoized
版本,該回調函數僅在某個依賴項改變時才會更新。當你把回調函數傳遞給經過優化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子組件時,它將非常有用。 useCallback(fn, deps)
相當于 useMemo(() => fn, deps)
。
最終使得onClick
的引用始終不變但是!
onClcik
這個方法有需要保證每次都要拿到最新的、正確的text,所以他的deps
中就自然是設置了text
—— 壞了,“又回到最初的起點~”。隨著每一次keystroke
,onClick
又變成了上面的情況:
Always a different function
但你又不能將其從deps
中移除,移除了他就只能拿到text
的初始值,失去了他本該有的功能...
useEvent
就是為了解決此類問題,所以他干脆不要deps
了,他就是一直返回一個相同的函數引用,哪怕text
發生變化。當然,保證它也能拿到最新的、正確的**text**
。
function Chat() { const [text, setText] = useState(''); // ? Always the same function (even if `text` changes) const onClick = useEvent(() => { sendMessage(text); }); return <SendButton onClick={onClick} />; }
現在好了:
onClick 的引用始終是同一個
保證每次都能拿到最新的、正確的 text
當然還有其他一些場景,但是大致需求原理相同,就是不想讓A
因為b
變化而總是重新加載,但是又因為要拿到b
恰當的值,所以deps
中必須b
,導致不得不重新加載,掉進了“圈圈圓圓圈圈~”的陷阱。更多場景這里就不再贅述。更多案例可查看文末的學習資源~
總而言之,用useEvent
給他裹上就是香,就是可以同時達到上面兩個效果:
引用不變
拿到恰當的值
說了這么多,我們來看看他這是咋做到的
他大概是這么個形狀:(不是源碼就長這樣的意思嗷)
// (!) 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); }, []); }
useRef:
useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化為傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命周期內持續存在。
這里通過 useRef 保存回調函數handler
到handlerRef.current
,然后再在 useCallback 中從handlerRef.current
來取函數再調用,這樣避免了直接調用,跳出了閉包陷阱。并且不出意外的話handler
在整個生命周期內持續存在,也就是只有一個引用。
這個 useLayoutEffect 可能沒那么常用,我們來看看這是啥嘞
其函數簽名與 useEffect 相同,但它會在所有的 DOM 變更之后同步調用 effect。可以使用它來讀取 DOM 布局并同步觸發重渲染。在瀏覽器執行繪制之前,useLayoutEffect 內部的更新計劃將被同步刷新。
回顧一下 useEffect
默認情況下,effect 將在每輪渲染結束后執行
好了,現在我給你用一個字總結一下兩者區別,useLayoutEffect
更“快”!這個“塊”不是速度更快,而是他“搶跑”了哩。useLayoutEffect
是在render
之前同步執行,useEffect
在render
之后異步執行,這里就是保證useLayoutEffect
里的回調肯定比useEffect
更早前被調用、被執行。
前面說到
useCallback(fn, deps)
相當于 useMemo(() => fn, deps)
。
文檔里是這樣說 useMemo 的:
記住,傳入 useMemo 的函數會在渲染期間執行。請不要在這個函數內部執行與渲染無關的操作,諸如副作用這類的操作屬于 useEffect 的適用范疇,而不是 useMemo。
也就是他是在render
時執行的,也就是保證了賦值handler
給handlerRef.current
是在前面發生
這里返回的是一個useCallback
包裹后 memoized
函數,其中從handlerRef.current
中獲取函數,并且deps
為[]
,也就是說他不會再次更新。
感謝各位的閱讀,以上就是“useEvent降低Hooks負擔的原生Hook分析”的內容了,經過本文的學習后,相信大家對useEvent降低Hooks負擔的原生Hook分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。