When to use useCallback - ReactJS?
Tags: react, usecallback, javascript
Published: 2019-10-23 16:18:21

> This post does not have a Yes/No answer, but tries to explain from a begineer standpoint why this hook is part of the official roster.

Let us start the story with two components:

1. Parent
2. Child

{% stackblitz usecallback-starter %}

The parent component has a button that increments the count state in the same component while Child component will have nothing to do with it.

Note the console logs as you click re-render. Both child and parent will re-render with logs:

```
re-render parent component
re-render child component.
```
even though the child component has nothing to do with the state at all.

Now, we have to prevent the Child component from rerendering. Keeping the functional component we can use [React.memo](https://reactjs.org/docs/react-api.html#reactmemo) to achieve this. The child component will become:

```jsx
import React, { memo } from "react";

const Child = memo(({ reset }) => {
   // same content as earlier
});

```
Without the second argument, `memo` will do a shallow comparison of props:

```js
if(prevProps !== props) {
  rerender();
} else {
  // don't
}

```
You can check the logs now and see that it does not update the child component on parent rerender. It only updates the parent component with the log:

```
re-render parent component
```

Now, the requirements have progressed and we have to house a Reset button for count inside the `Child` component.

This would refractor child to:

```jsx
import React, { memo } from "react";

const Child = memo(({ reset }) => {
  console.log("re-render child component.")
  return (
    <div>
      <p>child component which resets count</p>
      <button onClick={reset}>Reset Count</button>
    </div>
  );
});

export default Child;
```

For the reset function we have to refractor the parent to:

```jsx
const Parent () => {
  const [count, setCount] = useState(0);
  console.log("re-render parent component");

  const resetCount = () => {
    setCount(0);
  };
  return (
    <main>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count=>(count+1))}>Increment</button>
      <Child reset={resetCount} />
    </main>
  )
}
```
Now you can click on the reset button to reset the count to 0. But you will notice that the `memo` magic that we applied earlier is not working anymore. The logs suggest that both child and parent are being rerendered. 
Why is this happening?

As we mentioned earlier, `memo` depends on the referential equality of `prevProps` and `props` to work. But the `resetCount` function is being created on every render of `Parent` and hence `prevProps` and `props` is not the same anymore (even though they are). 

Now, to apply the `memo` magic again, we need to make sure that `resetCount` function is not unnecessarily recreated on every render of `Parent`. This is exactly what `useCallback` helps us to do. 

```js
const resetCount = useCallback(() => {
    setCount(0);
}, [setCount]);
```
`useCallback` will always return the same instance of the function on re-renders and would refresh only when dependencies change. Note the second argument of `useCallback`, this is very similar to the `useEffect` hook and refers to the dependecies that should trigger a reintialisation of the function inside `useCallback` hook.

Finished Demo:

{% stackblitz usecallback-finished %}

> Crossposted from [my blog](https://blog.agney.dev/when-to-use-usecallback/)

Extended Reading:

1. [useCallback Docs](https://reactjs.org/docs/hooks-reference.html#usecallback)
2. [When to use useMemo and useCallback - Kent C Dodds](https://kentcdodds.com/blog/usememo-and-usecallback)
3. [How to read an often-changing value from useCallback?](https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback)
4. [Are Hooks slow because of creating functions in render?](https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render)