When to use React.useCallback Hook

rakshit

Rakshit

Posted on August 1, 2022

When to use React.useCallback Hook

When I first read about the useCallback hook I thought I had a great weapon with me to optimise my React app's performance and started using it on every damn function without understanding the limitations or may be I should call it the right concept behind it.

Before diving deep into this topic, let us first understand on a high level what exactly is the useCallback hook.

So, basically useCallback hook takes a function and a dependency array. It returns the memoized function. A new memoized value of this function is created whenever the value or references of the elements in the dependency array change.

What if you do not wrap a function using useCallback?

When you don't wrap a function with useCallback, whenever the component is re-rendered a new instance of the function is created(The function is given a new memory location).

Also, make a note of the below snippet.

function add() {
  return (a, b) => a + b;
}
const add1 = add();
const add2 = add();

add1(1, 2); // 3
add2(1, 2); // 3

add1 === add2; // false
Enter fullscreen mode Exit fullscreen mode

In the above snippet you can see that though add1 and add2 are created from the same function declaration and give the same output they are not the same because the references of these two functions are different.

When to use useCallback?

Let us consider an example.

function Child({ handler }) {

  return (
    <div onClick={handler}>
        Click Me
    </div>
  );
}

export default React.memo(Child)
Enter fullscreen mode Exit fullscreen mode

Below is the Parent component

export default function ParentComponent() {
  const [state, setState] = useState(false);
  const [dep] = useState(false);

  const handler = useCallback(
    () => {
      console.log("You clicked handler")
    },
    [dep]
  );
  const statehanddler = () => {
    setState(!state);
  };
  return (
    <>
      <button onClick={statehanddler}>State Change</button>
      <Child handler={handler} />
    </>
  );
Enter fullscreen mode Exit fullscreen mode

In the above example we have wrapped the Child component with React.memo which means it will re-render the child component only if the props to it change.

handler is passed as a prop to the Child component.

Let us assume we did not use useCallback in the above example.

In this case whenever we click the State Change button the value of state is changed and the parent component is re-rendered. Since, at every re-render there will be a new instance of every function created we would have a new instance of the handler function.

Now, what would happen to the child component? Will it re-render?

In the add example I have shown you how does function equality works. By referring to it we can say that the child component will re-render because the handler prop now has a new reference. This means that even when we wrap the component with React.memo we are a re-rendering the child component.

Assuming we are using useCallback

useCallback hook here will memoize the function passed to it as an argument and it will only create a new instance of the memoized function if the value or reference to an element in the dependency array changes.

So, clicking on the State Change button will change the value of the state variable state but the value inside the dependency array(dep) remains same. Hence, there is no new instance of handler created and the child component will not re-render.

When not to use useCallback?

useCallback has its own downsides. There are times when using useCallback makes no sense.

Let us take an example

export default function Parent() {

  const clickHandler = useCallback(() => {
      console.log('Click event')
  }, [])

  return <Child onClick={clickHandler} />
}

const Child = ({ clickHandler }) => {
  return <button onClick={clickHandler}>Child Component</button>
}
Enter fullscreen mode Exit fullscreen mode

In the above example using useCallback makes no sense since we are creating clickHandler function on every re-render. Also, optimization might cost us more here because of the useCallback check we have to do on every re-render(Recreation of inline functions is generally cheap).

Conclusion

useCallback memoizes functions instead of values, to prevent recreation upon every render. It helps us avoid unnecessary re-rendering and improves performance.

We also should be careful while using useCallback because it can cost us a lot if we do not scan our components well before using it.

I hope this helps. If you have any questions and suggestions reach me out on Github and LinkedIn.

Follow me on Twitter

Have a nice day :)

💖 💪 🙅 🚩
rakshit
Rakshit

Posted on August 1, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related