返回一個 memoized 值
// useMemo返回一個值(可以是函數)
const computeExpensiveValue = (a, b) => {
return a + b;
}
const memoizedValue = useMemo(computeExpensiveValue(a, b), [a, b]);
// useCallbak返回一個函數
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
- useMemo和useCallback解決的因函數更新而渲染自己的問題
- 它僅會在某個依賴項改變時才重新計算 memoized 值
- 傳入
useMemo
的函數會在渲染期間執行 - 副作用屬于
useEffect
的適用范疇,而不是useMemo
- 如果沒有提供依賴項數組,
useMemo
在每次渲染時都會計算新的值 -
useCallback(fn, deps)
相當于useMemo(() => fn, deps)
。
與React.memo的區別
- react.memo解決的是因父組件state變化因為的自組件重復刷新的問題
- React.memo的用法
function MyComponent(props) {
}
function areEqual(prevProps, nextProps) {
// 手動判斷是否需要重新渲染
// return true不渲染,false則渲染
}
export default React.memo(MyComponent, areEqual);
問題
-
React.memo
第二個參數不用的話默認就 props 淺比較 -
React.memo
在跟某些hooks配合使用的情況下會失效,例如在組件內部使用useState
,useReducer
,useContext
,當 state 或者 context 改變時,組件還是會重新渲染
import React, { useState } from "react";
function Child1(props) {
console.log("執行子組件1了");
return <div>子組件1上的n:{props.value}</div>;
}
function Child2(props) {
console.log("執行子組件2了");
return <div>子組件2上的m:{props.value}</div>;
}
const MChild1 = React.memo(Child1, () => {
return false; // 失效
}); // compare失效會直接導致整個React.memo失效
const MChild2 = React.memo(Child2);
export default function App() {
const [n, setN] = useState(0);
const [m, setM] = useState(10);
console.log("執行最外層盒子了");
// 這里與是否是箭頭函數無關
function addM() {
setM(m + 1);
}
// 只要是復雜類型,都會渲染
const test = { a: 134 }; // 如果test是簡單類型,則不會重復渲染
return (
<>
<div>
最外層盒子
<MChild1 value={n} test={test}/>
<MChild2 value={m} addM={addM} />
<button
onClick={() => {
setN(n + 1);
}}
>
n+1
</button>
<button
onClick={addM}
>
m+1
</button>
</div>
</>
);
}
問題
- 自定義的compare函數失效,導致整個React.memo失效
- App重新執行了,它會修改復雜數據類型的地址,從而引發重新渲染
- useMemo解決addN和addM地址變更引發的重復渲染的問題
import React, { useState, useMemo, useCallback, memo } from "react";
const Child1 = memo((props) => {
console.log("執行子組件1了");
return <div>子組件1上的n:{props.value}</div>;
})
const Child2 = memo((props) => {
console.log("執行子組件2了");
return <div>子組件2上的m:{props.value.m}</div>;
})
export default function App() {
const [n, setN] = useState(0);
const [m, setM] = useState({ m: 1 });
console.log("執行最外層盒子了");
const addN = useMemo(() => {
return () => {
setN(n + 1);
};
}, [n]);
const addM = useCallback(() => {
setM({ m: m.m + 1 });
}, [m]);
return (
<>
<div>
最外層盒子
<Child1 value={n} click={addN} />
<Child2 value={m} click={addM} />
<button onClick={addN}>n+1</button>
<button onClick={addM}>m+1</button>
</div>
</>
);
}