Are you using useCallback properly 🤔
Tags: javascript, react, performance
Published: 2022-03-20 20:16:34

I didn't up until recently.
On the project my team is working on, we used `useCallback` for every function prop passed to the child components.
This approach doesn't give you benefits as you may expect.

Our code looked like this (not literally 😀)

```javascript
const ParentComponent = () => {
  ...
  const onClick = useCallback(() => console.log('click'), [])  

  return <ChildComponent onClick={onClick} />
}

const ChildComponent = ({ onClick }) => {
  return <button onClick={onClick}>Click me</button>
}
```
Approach without `useCallback`
```javascript
const ParentComponent = () => {
  ...
  return <ChildComponent onClick={() => console.log('click')} />
}
```
The benefits of the first approach compared to the second one are minimal and in some cases considering the cost of `useCallback` the second approach is faster.

_**The thing is creating and destroying functions on each rerender is not an expensive operation as you may think and replacing that with `useCallback` doesn't bring much benefits.**_

Another reason why we always used the `useCallback` hook is to prevent the child component rerender if its props didn't change but this was wrong because **_whenever the parent component rerenders the child component will rerender as well nevertheless the child props are changed or not._**
<br />

## React.memo

If you want to rerender the child component only when its props or state changed you want to use **[React.memo](https://reactjs.org/docs/react-api.html#reactmemo)**.
You can achieve the same with **_PureComponent_** or **_shouldComponentUpdate_** if you are working with class components instead of functional.

If we wrap ChildComponent from our first example with `React.memo`

```javascript
const ChildComponent = React.memo(({ onClick }) => {
  return <button onClick={onClick}>Click me</button>
})
```
when the ParentComponent rerenders and props of the ChildComponent don't change, the ChildComponent will not rerender.

**This gives us a good insight when we should use `useCallback` hook.
`useCallback` should be used in combination with the `React.memo`.**

I will not say that should be always the case, you can use useCallback without React.memo if you find it useful but in most cases, those two should be the pair. ❤
<br />

##When to use React.memo

There are no clear instructions on when to do it, someone thinks you should use it always and I'm for the approach "measure performance of your component and optimize it with `React.memo` if needed".

**The components which you can wrap with `React.memo` by default are those with a lot of children like tables or lists.**

Now we will take a look at an example.
You can clone it and try it by yourself from here [https://gitlab.com/markoarsenal/use-callback](https://gitlab.com/markoarsenal/use-callback).

It looks like this (very creative 😀)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5frmomvlig4qgmzv9lu.png)

We have a long list of comments (a good candidate for React.memo) and we have the counter button on the top whose main purpose is to trigger the rerender.

The code looks like this
```javascript
const Home = () => {
  const [counter, setCounter] = useState(0);
  const onClick = useCallback(() => console.log("click"), []);

  return (
    <Profiler
      id="Home page"
      onRender={(compName, mode, actualTime, baseTime) =>
        console.log(compName, mode, actualTime, baseTime)
      }
    >
      <main className="max-w-5xl p-8 m-auto">
        <div className="flex justify-center mb-8">
          <button
            onClick={() => setCounter(counter + 1)}
            className="px-3 py-1 border border-gray-500"
          >
            Update {counter}
          </button>
        </div>
        <Comments comments={comments} onClick={onClick} />
      </main>
    </Profiler>
  );
};
```
You can notice `Profiler` component as a root component, it's this one [https://reactjs.org/docs/profiler.html](https://reactjs.org/docs/profiler.html).
We are using it to measure rendering times.
You can notice `onRender` callback, we are logging a couple of things inside but the most important are **actualTime** and **baseTime**. The **actualTime** is the time needed for component rerender and **baseTime** is the time to rerender component without any optimizations. So if you don't have any optimizations within your component **actualTime** and **baseTime** should be equal.

`Comments` component looks like this (notice that is wrapped with React.memo)
```javascript
const Comments = ({ comments, onClick }: CommentsProps) => {
  return (
    <section>
      {comments.map((comment) => {
        return (
          <Comment
            {...comment}
            className="mb-4"
            onClick={onClick}
            key={comment.id}
          />
        );
      })}
    </section>
  );
};

export default memo(Comments);
```

Now I will run our example with 500 comments in Chrome, hit the "Update" button a few times to cause rerender and post results here.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqnj79k3b2sgfucoh5nx.png)

So, on every rerender we are saving around **30ms** which is considerable.

Let's try one more thing, instead of the list of the comments to render one, memoized comment and see what measurements are.

```javascript
{/* <Comments comments={comments} onClick={onClick} /> */}
<Comment {...comments[0]} onClick={onClick} />
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ellrirexwaacs6xsi8j.png)

Still, we have time savings but they are neglecting which means that React does not have trouble rerendering those small and simple components, and memoizing those doesn't have much sense.
On the other hand memoizing component that contains a lot of children is something you can benefit from.

Hope you enjoyed reading the article and that now you have a better overview of `useCallback` and `React.memo` and when to use them.