useCallback把匿名回調“存”起來
避免在component render時候聲明匿名方法,因為這些匿名方法會被反復重新聲明而無法被多次利用,然后容易造成component反復不必要的渲染。
在Class component當中我們通常將回調函數聲明為類成員:
class MyComponent extends React.Component { constructor(props) { super(props); this.clickCallback = this.clickCallback.bind(this); } clickCallback() { // ... } render() { return <button onClick={this.clickCallback}>Click Me!</button>; } }
使用useCallback hook就可以避免bind操作:
function MyComponent(props) { const clickCallback = React.useCallback(() => { // ... }, []); return <button onClick={clickCallback}>Click Me!</button>; }
useCallback緩存函數
const fnA = useCallback(fnB, [a])
上面的useCallback會將我們傳遞給它的函數fnB返回,並且將這個結果緩存;當依賴a變更時,會返回新的函數。既然返回的是函數,我們無法很好的判斷返回的函數是否變更,所以我們可以借助ES6新增的數據類型Set來判斷,具體如下:
import React, { useState, useCallback } from 'react';
const set = new Set();
export default function Callback() {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
console.log(count);
}, [count]);
set.add(callback);
return <div>
<h4>{count}</h4>
<h4>{set.size}</h4>
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<input value={val} onChange={event => setVal(event.target.value)}/>
</div>
</div>;
}
我們可以看到,每次修改count,set.size就會+1,這說明useCallback依賴變量count,count變更時會返回新的函數;而val變更時,set.size不會變,說明返回的是緩存的舊版本函數。
使用場景:
有一個父組件,其中包含子組件,子組件接收一個函數作為props;通常而言,如果父組件更新了,子組件也會執行更新;但是大多數場景下,更新是沒有必要的,我們可以借助useCallback來返回函數,然后把這個函數作為props傳遞給子組件;這樣,子組件就能避免不必要的更新。
import React, { useState, useCallback, useEffect } from 'react';
function Parent() {
const [count, setCount] = useState(1);
const [val, setVal] = useState('');
const callback = useCallback(() => {
return count;
}, [count]);
return <div>
<h4>{count}</h4>
<Child callback={callback}/>
<div>
<button onClick={() => setCount(count + 1)}>+</button>
<input value={val} onChange={event => setVal(event.target.value)}/>
</div>
</div>;
}
function Child({ callback }) {
const [count, setCount] = useState(() => callback());
useEffect(() => {
setCount(callback());
}, [callback]);
return <div>
{count}
</div>
}
不僅是上面的例子,所有依賴本地狀態或props來創建函數,需要使用到緩存函數的地方,都是useCallback的應用場景。
useEffect、useMemo、useCallback都是自帶閉包的。也就是說,每一次組件的渲染,其都會捕獲當前組件函數上下文中的狀態(state, props),所以每一次這三種hooks的執行,反映的也都是當前的狀態,你無法使用它們來捕獲上一次的狀態。對於這種情況,我們應該使用ref來訪問。
