# React Hook - useCallback ###### tags: `Javascript, React` > 1. 使用起來跟 useMemo 非常像,一樣是可以確保 referencial equality ,確保返回的函式不會因為任意的畫面 re-render 而被重新創造出來 > 2. 第二個比較常用到的地方則是當創造一個 function 時非常非常慢時 > 3. 與 useMemo 最大的差異是 useCallback 返回其函式本身 / useMemo 返回其函式的值 # 範例說明 這次的 DEMO 主要會操作這個 input form 主要改變數字的時候下方的 list 也會同時顯示並且顯示 本身, +1 , +2 並且可以更改 backgroundColor 藉由按鈕 toggle theme 達成 ![](https://i.imgur.com/rSaIiV6.png) ## App.js ```javascript= import './App.css'; import React,{useState} from 'react'; import List from './List'; export default function App() { const [number, setNumber] = useState(1); const [dark, setDark] = useState(false); const getItems = () => { return [number, number + 1, number + 2]; } const theme = { backgroundColor:dark? '#333':'#FFF', color:dark? '#FFF': '#333', marginLeft:'3rem', marginTop:'3rem' } return ( <div style={theme}> <input type="number" value={number} onChange={e => setNumber(parseInt(e.target.value))} /> <button onClick={() => setDark(prevDark => ! prevDark)}>Toggle theme</button> <List getItems ={getItems}></List> </div> ); } ``` ## List.js ```javascript= import React,{useState,useEffect} from 'react' export default function List({getItems}) { const [items, setItems] = useState([]) useEffect(() => { setItems(getItems()) console.log('Updating Items'); }, [getItems]) return items.map(item => <div key={item}>{item}</div>) } ``` 在 List.js 內有使用 useEffect 來操作當 getItems 更新了就會印出字樣 Updating Items ![](https://i.imgur.com/GVD2bY4.png) 可是問題來了,當我使用 toggle theme 按鈕時他卻也觸發了 ![](https://i.imgur.com/rZUX6l5.png) 然而會發生這種情況是因為畫面的 re-render (更新 state dark )的時候 getItems function 都會被重新創造出來,所以儘管裡面的數字相同,但是 function 是新的所以 useEffect 會再把字樣 log 出來一次 在這種情況下就可以使用 useCallback (這邊的使用情境跟 useMemo 很像),因為它可以監聽變數,只有在變數改變的情況下才會重新創造 getItems function ```javascript= const getItems = useCallback(() => { return [number, number + 1, number + 2]; },[number]) ``` 所以當使用完畢後,我們 toggle theme 就算 re-render 畫面,也不會在觸發創造新的 getItems function 因為它已經使用 useCallback 監聽變數 number 了 ## useCallback 以及 useMemo 最大的區別 > useMemo: Returns and stores the calculated value of a function in a variable > useCallBack: Returns and stores the actual function itself in a variable useMemo 會返回指派給變數的函式的值 useCallback 則是會返回指派給變數的函式的本身 所以你可以這樣操作 useCallback ```javascript= const getItems = useCallback((incrementor) => { return [number+ incrementor, number + 1+incrementor, number + 2+incrementor]; },[number]) ``` 因為返回的是函式本身所以是可以代入參數進去使用的 ```javascript= import React,{useState,useEffect} from 'react' export default function List({getItems}) { const [items, setItems] = useState([]) useEffect(() => { setItems(getItems(50)) console.log('Updating Items'); }, [getItems]) return items.map(item => <div key={item}>{item}</div>) } ``` 呈現結果如下 ![](https://i.imgur.com/NRq0Vj6.png) 所以同樣的你會使用到 useCallback 最主要的情況也是在你要注意 referencial equality 的時候,因為每次 re-render 的時候都會創造新的 function 因此需要使用到 useCallback 來確保只有在監聽的變數更新時,才會更新 function 以這邊的例子來說明的話就是,確保了只要不是 number 更新的情況下, getItems 的 funtion 是不會被改變的因此可以確保它的 referencial equality ## 第二種使用情景就是當創造一個 function 時非常非常慢 如下方的範例可以看出,要執行這個 slowFunction 顯然需要非常多的時間,所以我們不能在每次 re-render 畫面的時候都觸發重新創造這個函式,因此使用 useCallback 來確保只有當 number 更新時才會觸發重新創造這個 function 增進其效能 ```javascript= function slowFunction(num){ console.log('call slow function'); for(let i=0; i<1000000000; i++ ){} return num*2; } const doubleNumber = useCallback(()=>{ returm slowFunction(number )},[number]) ```