React函式元件效能優化之useEffect、useMemo、useCallback、React.memo
阿新 • • 發佈:2020-12-30
前言
react函式元件不想類元件那樣有生命週期函式,以及state。但是我們可以通過hook來優化我們的效能。
一個元件重新重新渲染,一般三種情況:
- 要麼是元件自己的狀態改變
- 要麼是父元件重新渲染,導致子元件重新渲染,但是父元件的 props 沒有改變
- 要麼是父元件重新渲染,導致子元件重新渲染,但是父元件傳遞的 props 改變
React.memo
function Test() {
const [value,setValue] = useState(0)
return ( <div>
{ value}
<button onClick={()=>{setValue(1)}}>changeValue</button>
<Testson name='lisi' />
</div>
)
}
function Testson(props) {
console.log('Testson 重新渲染了')
return (
<p>{props.name}</p>
)
}
在上面的程式碼執行的時候,Testson的props的值沒有改變,只有父元件的value的值改變了。重新渲染了父元件,也重新渲染了子元件。
通過react.memo包裹子元件,在props不變的情況下,Testson是不會二次渲染的。修改後的程式碼如下
const Testson = React.memo(function(props) {
console.log('Testson 重新渲染了')
return (
<p>{props.name}</p>
)
})
react.memo高階用法
const Testson = React.memo(function(props) {
console.log('Testson 重新渲染了')
return (
<p>{props.name}</p>
)
},(preProps,nextProps) => {
//判斷邏輯
return true //false、true ===》false渲染、true不渲染
})
用法和類元件的shouldComponentUpdate()差不多;
useCallBack
我們再新增一個需求,就是通過Testson子元件修改父元件的myKey。程式碼如下。
function Test() {
const [value,setValue] = useState(0)
const [myKey,setMyKey] = useState('mykey')
return ( <div>
{myKey}---{value}
<button onClick={()=>{setValue(1)}}>changeValue</button>
<Testson name='lisi' click={() => {setMyKey('myKey has changed')}}/>
</div>
)
}
const Testson = React.memo(function(props) {
console.log('Testson 重新渲染了')
return (
<div>
<button onClick={props.click}>changeMyKey</button>
<p>{props.name}</p>
</div>
)
再以上的程式碼執行中,點選changeValue按鈕還是會重新渲染子元件,雖然props並沒有改變,這是一個不必要的渲染。為什麼會重新渲染來呢?這是因為父元件重新渲染了,所以props的值改變了,這個值是props.click,因為這是一個函式,每次渲染都會重新建立,所以它的指向也就不同了。就像建立了兩個物件一樣。我們通過usecCallback來處理;
function Test() {
const [value,setValue] = useState(0)
const [myKey,setMyKey] = useState('mykey')
const changeMykey = useCallback(() => {setMyKey('myKey has changed')},[])
return ( <div>
{myKey}---{value}
<button onClick={()=>{setValue(1)}}>changeValue</button>
<Testson name='lisi' click={changeMykey}/>
</div>
)
}
const Testson = React.memo(function(props) {
console.log('Testson 重新渲染了')
return (
<div>
<button onClick={props.click}>changeMyKey</button>
<p>{props.name}</p>
</div>
)
})
通過以上程式碼就能解決了子元件不必要的渲染。
useEffect
在呼叫 useEffect 後,相當於告訴 React 在每一次元件更新完成渲染後,都呼叫傳入 useEffect 中的函式,包括初次渲染以及後續的每一次更新渲染。
- useEffect(effectCallback: () => void, deps: any[]) 接收兩個引數,第二個引數依賴項是可選的,表示這個 effect 要依賴哪些值。
- 有時候我們並不想每次渲染 effect 都執行,只有某些值發生變化才去執行 effect,這個時候我們可以指定這個 effect 的依賴列表,可以是一個也可以幾個,當其中列表中的某一個值發生變化,effect 才會執行。
- 第一個引數的返回值,會在元件解除安裝時執行,相當於 componentWillUnmount,可以清理定時器,移除事件監聽,取消一些訂閱。
- 當第二個引數為一個空陣列如果第二個沒傳引數則每次元件更新完成渲染後都呼叫useEffect裡面的回撥函式時,相當於 componentDidMount 和 componentWillUnmount,表明這個 effect 沒有任何依賴,只在首次渲染時執行。
useMemo
useMemo可以快取計算。學過vue的同學都知道vue有computed可以快取計算的值。react和computed差不多。設計出來都是為了快取計算;
function App() {
const [num, setNum] = useState(0);
function expensiveFn() {
let result = 0;
for (let i = 0; i < 10000; i++) {
result += i;
}
console.log(result)
return result;
}
const base = useMemo(expensiveFn, []);
return (
<div className="App">
<h1>count:{num}</h1>
<button onClick={() => setNum(num + base)}>+1</button>
</div>
);
}
以上程式碼執行。不管點多少次+1都只會輸出一次result,也就是expensiveFn值計算一次。通過useMemo快取了expensiveFn的計算結果。