前言
useMome和useCallback實現原理完全一致
useMemo(() => fn, []); //等效於 useCallback(fn, []);
不同的點
useCallback
//第一個參數接收一個函數,useCallback調用后返回一個新的被緩存的記憶函數 useCallback(fn, []); //return 一個新的function
useMemo
//第一個參數接收一個函數,useMemo調用后返回第一個參數函數return的被緩存的記憶的值 useMemo(() => fn, []); //return 一個fn的記憶function useMemo(() => obj, []); //return 一個obj的記憶Object
作用影響
測試代碼
function Home() { const [state, setState] = useState("initialization"); //普通函數 const fn = () => { console.log("普通函數輸出:", state); }; //記憶函數,這里第二個參數設置為[],表示不依賴任何值,只在組件初始化時創建memoizedFn,組件更新時不更新memoizedFn const memoizedFn = useCallback(() => { console.log("memoized函數輸出:", state); }, []); //組件Home,mount 和 update時都執行 fn(); memoizedFn(); const update = () => { setState("initialization" + new Date().getTime()); }; return ( <div> <div>state值:{state}</div> <button onClick={update}>改變state</button> </div> ); }
作用
1.在組件初始化時,fn 和 memoizedFn 都會拿取到state的最新值initialization嗎?
2.在組件更新后只要依賴的項沒有發生變化,那么memoizedFn輸出的結果永遠是舊值?
3.如果使用的是非響應式(useState())的普通變量,memoizedFn還會保留它嗎?
4.多個memoizedFn嵌套使用,該如何緩存結果?
驗證
1.【驗證1】在組件初始化時,fn 和 memoizedFn 都會拿取到state的最新值initialization

可以看到在初始化時,可以看到普通函數 fn 和 記憶函數memoizedFn 都打印出了state的初始值:inittialization
2.【驗證2】在組件更新后只要依賴的項沒有發生變化,那么memoizedFn輸出的結果永遠是舊值

可以看到當組件更新后,普通函數fn 讀取到了最新的state值:initialization1640850521426,而memoizedFn函數,輸出的還是組件創建時的舊state值:inittialization,這就意味着當依賴項(我們這里設置的:[]),沒有改變時無論我們執行多少次memoizedFn函數,始終輸出的都是上一次更新或者時創建時的舊值,在memoizedFn使用的所有變量(state),都是被緩存的舊值
3.【驗證3】.如果使用的是非響應式(useState())的普通變量,memoizedFn還會保留它嗎
修改代碼
function Home() { const [state, setState] = useState("initialization"); let normal = "init noraml"; //增加normal變量 //普通函數 const fn = () => { console.log("普通函數輸出:", state, "noraml:", normal); //增加輸出 }; //記憶函數 const memoizedFn = useCallback(() => { console.log("memoized函數輸出:", state, "noraml:", normal); //增加輸出 }, []); //組件Home,mount 和 update時都執行 //... const update = () => { const date = new Date().getTime(); setState("initialization" + date); normal = "init noraml" + date; //增加輸出 }; //... }

可以看到,在第一次更新state時,memoizedFn 並沒有緩存noraml的舊值init noraml,而在第二次更新時,使用的是第一次更新時的緩存值,這是一個很奇怪的點,官方上並沒有給出答案,但是卻推薦,不要在useCallback中使用外部的普通變量,盡量在useCallback類定義變量,以確保變量能得到我們預期的結果
const memoizedFn = useCallback(() => { let normal = "init noraml"; console.log("memoized函數輸出:", state, "noraml:", normal); //增加輸出 }, []);
4.【驗證4】多個memoizedFn嵌套使用,該如何緩存結果
修改代碼
function Home() { const [state, setState] = useState("initialization"); //記憶函數1 無任何依賴 const memoizedFn1 = useCallback(() => { console.log("memoized函數1 輸出:", state); console.log("memoized函數1調用memoizedFn2"); memoizedFn2(); }, []); //記憶函數1 無任何依賴 const memoizedFn2 = useCallback(() => { console.log("memoized函數2 輸出:", state); }, [state]); //組件Home,mount 和 update時都執行 memoizedFn1();
console.log("直接調用memoizedFn2"); memoizedFn2();
const update = () => { const date = new Date().getTime(); setState("initialization" + date); //使用setState改動state console.log("=====組件更新====="); }; return ( <div> <div>state值:{state}</div> <button onClick={update}>改變state</button> </div> ); }

可以看出,在memoizedFn1中的執行的memoizedFn2,即便memoizedFn2中設置的依賴[state]發生更新,memoizedFn2讀取的state仍是舊值,這就意味着在memoizedFn內部的函數,只要最外層的memoized不發生更新,那么內部函數使用的所有變量都為舊值
