React useCallback詳細(xì)使用教程
一、useCallback的作用
usecallback不是用來解決組件中有過多內(nèi)部函數(shù)導(dǎo)致的性能問題:
1.我們要知道,js創(chuàng)建一個函數(shù)的成本是非常小的,這點計算對于計算機(jī)來說是小case
2.其實使用useCallback會產(chǎn)成額外的性能:對deps的判斷
3.其實每次組件重新渲染時,都無所謂避免重新創(chuàng)建內(nèi)部函數(shù),因為即使useCallback的deps沒有變,它也會重新創(chuàng)建內(nèi)部函數(shù)作為useCallback的實參
那么,它的作用到底是什么?useCallback的作用其實是用來避免子組件不必要的reRender:
首先,假如我們不使用useCallback,在父組件中創(chuàng)建了一個名為handleClick的事件處理函數(shù),根據(jù)需求我們需要把這個handleClick傳給子組件,當(dāng)父組件中的一些state變化后(這些state跟子組件沒有關(guān)系),父組件會reRender,然后會重新創(chuàng)建名為handleClick函數(shù)實例,并傳給子組件,這時即使用React.memo把子組件包裹起來,子組件也會重新渲染,因為props已經(jīng)變化了,但這個渲染是無意義的
如何優(yōu)化呢?這時候就可以用useCallback了,我們用useCallback把函數(shù)包起來之后,在父組件中只有當(dāng)deps變化的時候,才會創(chuàng)建新的handleClick實例,子組件才會跟著reRender(注意,必須要用React.memo把子組件包起來才有用,否則子組件還是會reRender。React.memo是類似于class組件中的Pure.Component的作用)
對于這種deps不是經(jīng)常變化的情況,我們用useCallback和React.memo的方式可以很好地避免子組件無效的reRender。但其實社區(qū)中對這個useCallback的使用也有爭議,比如子組件中只是渲染了幾個div,沒有其他的大量計算,而瀏覽器去重新渲染幾個dom的性能損耗其實也是非常小的,我們花了這么大的勁,使用了useCallback和React.memo,換來的收益很小,所以一些人認(rèn)為就不用useCallback,就讓瀏覽器去重新渲染好了。至于到底用不用,此處不深入討論,我的建議是當(dāng)子組件中的dom數(shù)量很多,或者有一些大量的計算操作,是可以進(jìn)行這樣的優(yōu)化的。
以上都是討論的deps不會經(jīng)常改變的情況的優(yōu)化,而很多時候useCallback中的deps數(shù)組中的變量是會經(jīng)常改變的,這個時候我們用useCallback已經(jīng)沒啥意義了,反而會造成性能損耗(deps判斷)。有沒有什么辦法可以讓子組件不重新渲染,也能拿到父組件中handleClick函數(shù)中的最新state值呢?下面我們討論useRef,useReducer,usePersistFn這三種解決方法
二、useRef解決方案
為了讓子組件不進(jìn)行reRender,我們必須保證
1.父組件不會重新創(chuàng)建handleClick函數(shù)實例
2.handleClick函數(shù)能拿到最新的state
使用useRef
const [text, setText] = useState('Initial value');
const textRef = useRef(text);
const handleClick= useCallback(() => {
console.log(textRef.current);
}, []);
useEffect(() => {
console.log('update text')
textRef.current = text;
}, [text])
textRef在每次reRender時不會改變,這樣我們把handleClick傳給子組件,handleClick函數(shù)中每次都能拿到父組件中最新的state
三、useReducer解決方案
使用useReducer
function reducer(state, action) {
switch(action.type) {
case 'update':
return action.preload;
case 'childComponent':
// 要執(zhí)行的函數(shù)
return state;
}
}
export default function Index() { // 父組件
const [state, dispatch] = useReducer(reducer, 'Initial value');
return (
<>
<input value={state} onChange={(e) => dispatch({
type: 'update',
preload: e.target.value
})} />
<ChildComponent dispatch={dispatch} />
</>
)
}
//在 ChildComponent中,拿到dispatch,通過dispatch({type: 'childComponent' })的方式調(diào)用dispatch自帶memoize,所以子組件不會進(jìn)行 re-render
四、usePersistFn解決方案
usePersistFn是aooks庫中的一個鉤子函數(shù),它接收一個函數(shù),返回一個永遠(yuǎn)不變的函數(shù)引用,在這個函數(shù)中每次都能拿到最新的state值,看看usePersistFn的源碼:
function usePersistFn(fn) {
const fnRef = useRef(fn);
fnRef.current = fn;
const persistFn = useRef();
if (!persistFn.current) {
persistFn.current = function (...args) {
return fnRef.current.apply(this, args);
};
}
return persistFn.crrent;
}
這里它用了兩個useRef,保證返回的函數(shù)引用不變,并且每次函數(shù)內(nèi)部能拿到最新的state。
這里可以用useRef和useCallback到達(dá)同樣的效果:
function usePersistFn(fn) {
const fnRef = useRef();
fnRef.current = fn;
const persist= useCallback((...rest) => {
return fnRef.current(...rest);
}, []);
return persist
}
這種方法和上面的useRef解決方案差不多,只是封裝了起來而已
另外,本文介紹一下useContext使用的問題和優(yōu)化:
問題:當(dāng)context中的值改變時,只要使用useContext訂閱了context的組件,不管該組件用到的state改不改變,該組件都會reRender,此時用React.memo是沒有辦法優(yōu)化的。
優(yōu)化:
1.拆分context,把經(jīng)常改變的數(shù)據(jù)和不經(jīng)常改變的數(shù)據(jù)拆分開,在只使用穩(wěn)定數(shù)據(jù)的組件中,我們只使用stableContext
2.使用useMemo
const {state}= useContext(AppContext);
return useMemo(() => <span>data:{state.depData}</span>, [state.depData]);
3.如何有效減少使用useContext導(dǎo)致的不必要渲染
到此這篇關(guān)于React useCallback詳細(xì)使用教程的文章就介紹到這了,更多相關(guān)React useCallback內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react循環(huán)數(shù)據(jù)(列表)的實現(xiàn)
這篇文章主要介紹了react循環(huán)數(shù)據(jù)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
React useImperativeHandle處理組件狀態(tài)和生命周期用法詳解
React Hooks 為我們提供了一種全新的方式來處理組件的狀態(tài)和生命周期,useImperativeHandle是一個相對較少被提及的Hook,但在某些場景下,它是非常有用的,本文將深討useImperativeHandle的用法,并通過實例來加深理解2023-09-09
React中的useState和setState的執(zhí)行機(jī)制詳解
這篇文章主要介紹了React中的useState和setState的執(zhí)行機(jī)制,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
在react-router4中進(jìn)行代碼拆分的方法(基于webpack)
這篇文章主要介紹了在react-router4中進(jìn)行代碼拆分的方法(基于webpack),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
react-beautiful-dnd 實現(xiàn)組件拖拽功能
這篇文章主要介紹了react-beautiful-dnd 實現(xiàn)組件拖拽功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08

