2023-05-22
#ReactReact useCallback 筆記
#範例
#畫面說明:
- 在 input 輸入數字後,下面列表會顯示此 input 值加1、加2、加3的結果
- 點擊 toggle theme 按鈕可把畫面轉為亮/暗色模式

#Code說明:
- 父元件
- 父元件包含一個 input 和按鈕,並引入子元件
<List/> - 父元件有個
getDatafunction,我們就假裝它裡面會呼叫 api 獲取資料並 return 這筆資料 getDatareturn 的資料會依input值而改變- 把
getData作為 props 傳到子元件<List/>
import List from './List';
export default function App() {
const [input, setInput] = useState(0);
const [themeDark, setThemeDark] = useState(false);
const style = {
color: themeDark ? '#fff' : '#1b1b1b',
background: themeDark ? '#1b1b1b' : '#fff',
};
// 假裝它會呼叫 api、回傳資料
const getData = () => {
// getData return 的資料會依 input 值而改變
return [input + 1, input + 2, input + 3];
};
return (
<div className="App" style={style}>
<input
value={input}
type="number"
onChange={(e) => {
setInput(parseInt(e.target.value));
}}
/>
<button
onClick={() => {
setThemeDark(!themeDark);
}}
>
Toggle Theme
</button>
// getData 作為 props 傳入
<List getData={getData} />
</div>
);
}
- 子元件
- 子元件接收
getData這個 props,呼叫並使用getData回傳的資料 - 每次
getData改變時,就重新用setArray設置畫面所顯示的 list 項目,並印出 "get data" 文字
export default function List(props) {
const { getData } = props;
const [array, setArray] = useState([]);
useEffect(() => {
setArray(getData());
console.log('get data');
}, [getData]);
return (
<>
{array.map((item) => {
return <li key={item}>{item}</li>;
})}
</>
);
}
#發現問題
- input 值改變時,
getItemfunction 會跟著改變,因此在<List/>裡面的useEffect會被觸發,執行getData(),並印出 "get data",這很好理解 - 但是當按下 toggle theme 按鈕來切換主題時,也會觸發
useEffect,印出 "get data"
這代表 getData 在點擊按鈕時也改變了,原因是點擊按鈕時會改變 themeDark 狀態,然後觸發父元件的 re-render;父元件重新 render 時,就會創造一個新的 getData function。
#解決
使用 useCallback
useCallback用法跟useMemo一樣,有兩個參數:
第一個:callback function
第二個:是一個陣列,像 useEffect 那樣,當陣列裡某元素的值改變時,就會執行第一個參數的 function
// 原本寫法
const getData = () => {
return [input + 1, input + 2, input + 3];
};
// 改為
const getData = useCallback(() => {
return [input + 1, input + 2, input + 3];
}, [input]);
useCallback 跟 useMemo 比較
useMemo不能傳入參數,而useCallback可以useCallbackuseMemo都會接收一個 function,但useMemo會回傳此 function 的回傳值,而useCallback會回傳此 functionuseCallback(fn, [deps])就相當於useMemo(() => fn, [deps])
// 也可用 useMemo 寫法
const getData = useMemo(() => {
return () => {
return [input + 1, input + 2, input + 3];
};
}, [input]);
閱讀推薦: