# React: Hooks - useCallback

在 [React.memo](https://urlun0404.hashnode.dev/react-memo)的文章有提到，每次component更新(re-evaluate)都會產生長得一模一樣的新函式物件，所以當新的函式物件當作props傳入child component，child component會因為傳入的函式物件不同而跟著parent component更新。

那要如何讓component更新時不會產生新的函式物件？

答案就是用 `useCallback`。
<br/>

-----
## useCallback
`useCallback` 是React用來儲存函式物件的hook，並能保證每次component更新時使用的都是和前一次更新相同的函式，也就是同一個位址的函是物件。

```
// App.js (parent component)
import {useState, useCallback} from 'react';
import Me from './Me';
import Button from './Button';


export default function App(){
	console.log('App loads');

	const [isMe, setIsMe] = useState(false);
    
    // Use useCallback hook
	const addMe = useCallback((event) => {
	event.preventDefault();
	setIsMe((prevMe) => !prevMe);
	}, []);

	return (
		<>
			<h1>h1 always here</h1>
			<Me me={false}/>
			<Button onClick={addMe}>Click and Add Me!</Button>
		</>
	)
}
```
`useCallback` 讓component更新時不會創造一個長得一模一樣卻是儲存在不同地方的新函式，也因此每次傳入Button component的addMe函式props就會是相同的。
<br/>


此外，[這篇文章](https://dev.to/marcostreng/how-to-really-remove-eventlisteners-in-react-3och) 在討論如何正確地用  `removeEventListener` 移除事件監聽器，而這個問題的原因就是每次Component更新會產生看起來名稱相同、但其實是不同物件的函式，因此沒辦法正常使用 `removeEventListener`。其中一個解決辦法就是用 `useCallback` hook；而文章下面[留言](https://dev.to/stigkj/comment/1kmc3)有提到另一個辦法是使用`useEffect`來處理這個問題，而且會處理的會更好。

---
References
- [useCallback @React Docs](https://reactjs.org/docs/hooks-reference.html#usecallback)
- [React Hooks Tutorial - 26 - useCallback Hook](https://www.youtube.com/watch?v=IL82CzlaCys)
- [React - The Complete Guide (incl Hooks, React Router, Redux)](https://www.udemy.com/course/react-the-complete-guide-incl-redux/)
- [How to (really) remove eventListeners in React](https://dev.to/marcostreng/how-to-really-remove-eventlisteners-in-react-3och)
- [如何錯誤地使用 React hooks useCallback 來保存相同的 function instance](https://jason-memo.dev/posts/how-to-use-react-hooks-useCallback-wrongly-for-keeping-same-function-instance/)
- [Memoization @Wikipedia](https://en.wikipedia.org/wiki/Memoization)

-----
#### Live Demo
<iframe src="https://codesandbox.io/embed/example-usecallback-4poit4?expanddevtools=1&fontsize=12&hidenavigation=1&theme=dark"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="example-useCallback"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   />
