When to use useCallback?
Tags: react, javascript, tutorial, performance
Published: 2022-01-26 14:08:04

The usage of **`useCallback`** is something very controversial where it exists two groups of person:
- those who memoize everything 
- those who only memoize the strict necessary

In this article I expose you my rules which makes me decide to use `useCallback`.

----

## What is `useCallback`

`useCallback` is a native hook provided by **React**, it permits to give you a memoized callback.

### Why is it necessary?

As a quick reminder when developing with functional component, the body of the function is the render.

So if I define a function inside the component (render), this function will be redefined at every renders giving you a new references.

```jsx
function myComponent() {
  // At each renders I am redefined
  // I.E. I will have a new references
  const onClick = () => {
    console.log("I have been clicked");
  };

  return <button onClick={onClick}>Click me</button>;
}
```

### Is it a problem to redefines function?

My answer is simply NO.

Most of the time we don't care. This is not a problem for our javascript engine to do it, it's fast and no memory issue with that.

**So when do we care?**
Let me just a second I want you to show a quick implementation of the hook before :)

### A simplified implementation

The logic is pretty simple when you know how to implemt some memoization in JS. If it's not the case you can read [my article](https://www.romaintrotard.com/posts/function-result-memoization/) :)

But in the case of **React** there is no closure.

Th previous callback and dependencies are stored in the **Fiber node** of the component. This is stored within the key **`memoizedState`**.

In the next code template, I show you an implementation example:

```javascript
import shallowEqual from "./shallowEqual";

// Things stored in the React element
const memoizedState = {
  lastCallback: undefined,
  lastDependencies: undefined,
};

// In reality there is multiple implementation of 
// it behind the hood
// But it's a simplified example
function useCallback(callback, dependencies) {
  if (
    !shallowEqual(
      memoizedState.lastDependencies,
      dependencies
    )
  ) {
    memoizedState.lastCallback = callback;
    memoizedState.lastDependencies = dependencies;
  }

  return memoizedState.lastCallback;
}
```

As you can see a `shallowEqual` is used to compare the dependencies. If you want to know more about the different types of equality, do not hesitate to read [my article about it](https://dev.to/romaintrotard/different-types-of-equality-in-javascript-2mo6).

> **Warning:** In reality **React** has multiple implementation of the hooks in function of the lifecycle phase: `mount`, `update`, ...

And now let's see with a quick gif how to see this in a browser:

![memoizedState in the browser dev tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i6rut334bsnarezd44cv.gif)


> **Note:** In a next article I will try to explain more about the structure of a **Fiber node** but it takes me some time to make a nice post about it :)


> **Note:** Yep, it's memoized inside an array, but I put the data inside an object for simplicity.

----

## Reasons for me to use `useCallback`

### Performances issues

As usual, I will begin by told not to do premature optimization. Only do this when you have real performance problem in your application / component library.

For example if you have a component in your code base which has slow renders and that most of the time they can be prevent because it doesn't need to be re-render (no props change in reality).

In this case we will **memo** the component. And from here it's important that references do not change unnecessarily.

Now imagine that this component is a `Button`. Yeah it would probably not happen for a button, I know. But it's just an example ;)

So in this case it will be important that the `onClick` callback has a stable reference.

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

function App() {
  const onClick = useCallback(() => {
    // Doing some stuff here
  }, []);

  return (
    <MemoizedButton onClick={onClick}>
      Click me
    </MemoizedButton>
  );
}

function Button({ onClick }, children) {
  // Doing some heavy process here

  return <button onClick={onClick}>{children}</button>;
}

const MemoizedButton = React.memo(Button);
```

> **Warning:** If you do not put the `useCallback`, then the `React.memo` will be totally useless. 

And the reciprocal is also true. If you `useCallback` but do not `React.memo` the `Button` then instead you make your performance worse. 

**Why?**
 Because as we have seen at each render there is 2 callbacks that are in memory. 
Yep it's not dramatic, but by doing this, I find the codebase less readable.

---- 

### When putting it as a dependency

Another reason which makes me `useCallback` is when I need to put the callback in the dependency of `useEffect`, `useLayoutEffect` or `useCallback`.

```jsx
import { useCallback, useEffect, useState } from "react";
import apiCall from "./apiCall";

function App() {
  const [data, setData] = useState();

  const fetchData = useCallback(() => {
    apiCall().then(setData);
  }, []);

  useEffect(() => {
    // We fetch the data at mounting
    fetchData();
  }, [fetchData]);

  return (
    <div>
      <p>The data is: {data}</p>
      <button onClick={fetchData}>Refetch data</button>
    </div>
  );
}
```

> **Note:** As you can see in the example I use the callback in the `useEffect` and in an event handler.

If it was used only in the `useEffect`, I would have defined the method directly in it:

```javascript
useEffect(() => {
  const fetchData = () => {
    apiCall().then(setData);
  };

  // We only fetch the data at mounting
  fetchData();
}, [fetchData]);
```

----

### When doing public or generic hook

Another will be when I do some "public" hook, for example in a library, or a generic hook that could be used in multiple place. 
Then I will stabilize returned callbacks.

**Why do I do this?**

The reason is that I don't know where it will be used. It could be:
- in a useEffect/useCallback/useLayoutEffect then it will be required to have a stable reference
- in an event handler, then it's not required at all

So to satisfy both cases, I provide a stable reference :)

```javascript
import { useCallback } from "react";

export function usePublicHook() {
  return useCallback(() => {
    console.log("It's only an example");
  }, []);
}
```

But if I do a hook just to extract a specific logic from a component (for testing purpose and to make the component easier), and it can't be used in another one. Then I will only `useCallback` when it's necessary because I know the use case.

----

And here we go. That's how I use the hook **`useCallback`**, hoping that it can help you to have a better code base, because it makes the code more complicated to read.

To summarize:
- if I have performances issues
- if I used it as dependency of another hook (`useEffect`, `useLayoutEffect`, `useCallback`, ...)
- when I do a public / generic hook

I hope to see **React Forget** released as soon as possible (yep I'm dreaming), which will help us to stop wondering :) If you don't know what is React Forget, let's check [this video](https://www.youtube.com/watch?v=lGEMwh32soc&ab_channel=ReactConf2021).

Do you use `useCallback` in an other use case? If it's the case do not hesitate to put it in comment.

----

Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com).