在react函數(shù)組件中修改狀態(tài)會(huì)觸發(fā)整個(gè)函數(shù)組件的重載,重載過(guò)程中會(huì)導(dǎo)致函數(shù)中的方法重載和組件重新渲染,這個(gè)過(guò)程中有很多重載和重新渲染是不必要的,我們可以使用memo和useCallback方法來(lái)阻止不必要的性能消耗。
首先定義三個(gè)基礎(chǔ)組件,來(lái)觀察不使用上述方法之前的渲染情況:

在最外層父組件中聲明count狀態(tài)和更改count的方法setCount,并定義一個(gè)將count重置為0的方法傳遞給Foo組件,當(dāng)我們觸發(fā)count更改時(shí)整個(gè)App函數(shù)都會(huì)重載,重載過(guò)程中會(huì)導(dǎo)致reset方法也被重載,App組件當(dāng)中引入的Foo和Pure組件都會(huì)重新渲染。

如上圖所示,每次點(diǎn)擊按鈕都會(huì)觸發(fā)兩個(gè)子組件的重新渲染,但是事實(shí)上這兩個(gè)子組件的重新渲染是沒(méi)有必要的。
memo:
memo的作用是在組件重新渲染前確認(rèn)內(nèi)部傳入的組件是否需要重新渲染。
這里將Pure組件用memo方法進(jìn)行嵌套。

然后再次點(diǎn)擊按鈕更改狀態(tài)觸發(fā)App組件的重新渲染

可以看到這里再次進(jìn)行點(diǎn)擊,重新渲染的子組件就只有Foo了,由于Pure內(nèi)部沒(méi)有狀態(tài)和屬性更改,memo會(huì)判定該組件無(wú)需重新渲染
useCallback:
useCallback的作用是緩存一個(gè)函數(shù),并傳入相關(guān)的依賴(lài)項(xiàng),只有在依賴(lài)項(xiàng)改變的時(shí)候才會(huì)重載函數(shù)
我們首先給Foo也包裹memo方法

點(diǎn)擊按鈕更改狀態(tài)觸發(fā)App組件的重新渲染

發(fā)現(xiàn)即使嵌套了memo,F(xiàn)oo組件還是會(huì)不斷重新渲染,原因是我們從父組件傳入的reset在父組件重載的過(guò)程中也被重載了,新的reset !== 上次傳入的reset,由于屬性發(fā)生了更改,因此被認(rèn)為有必要進(jìn)行重新渲染,這時(shí)就應(yīng)該用useCallback將reset方法進(jìn)行緩存,阻止這種不必要的重新渲染。

此時(shí)再觸發(fā)狀態(tài)更改

兩個(gè)組件都只有初始化渲染,不再觸發(fā)重新渲染了。
附代碼:
import React, { memo, useCallback, useState } from "react";
// 最外層父組件
function App() {
const [count, setCount] = useState(0)
const reset = useCallback(() => {
setCount(0)
}, [])
return (
<div className="App">
<div>{count}</div>
<button onClick={() => setCount(count + 1)}>add</button>
<Foo reset={reset} />
<Pure />
</div>
);
}
// 接收函數(shù)
const Foo = memo(function Foo(props) {
console.log('Foo render...');
return (
<button onClick={props.reset}>
reset
</button>
)
})
// 純函數(shù)組件
const Pure = memo(function Pure() {
console.log('pureComponent render...');
return (
<div>pureComponent</div>
)
})
export default App;