DEV Community

Cover image for useCallback
Pritpal Singh
Pritpal Singh

Posted on

useCallback

What is useCallback?

useCallback is a React hook that helps optimize performance by memoizing a function. In simple terms, it remembers a function so that it doesn’t get recreated every time a component re-renders, which can prevent unnecessary work.

Think of memoization as caching a value so that it does not need to be recalculated.

Why do we need useCallback?

In React, whenever a component re-renders, everything inside it gets recreated, including functions. This can cause problems:

  • Performance issues: If the function is doing heavy work, re-creating it every render can slow down your app.
  • Unnecessary re-renders: If you pass a function as a prop to a child component, and that function gets recreated every time, it might trigger re-renders in the child component even if nothing else changes.

To avoid these issues, we use useCallback.

How does useCallback work?

The useCallback hook tells React: “Hey, remember this function and only recreate it if certain dependencies change.” This helps React to reuse the same function on subsequent renders unless there’s a need to recreate it.

Syntax of useCallback

const memoizedFunction = useCallback(() => {
  // function logic
}, [dependency1, dependency2]);
Enter fullscreen mode Exit fullscreen mode
  • useCallback takes two arguments:
    1. A function: This is the function that React should "remember."
    2. A dependency array: React will only recreate the function if any of the values in this array change.

Example Without useCallback

Let’s take a simple example where we have a button that increases a counter, and we have a function that prints the current count.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const logCount = () => {
    console.log('Current count:', count);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={logCount}>Log Count</button>
    </div>
  );
}

export default Counter;
Enter fullscreen mode Exit fullscreen mode

What’s the issue here?

In this example, every time the counter is updated, the Counter component re-renders, and the logCount function is recreated even though its logic hasn’t changed. This isn’t a big problem here because the function is small, but in larger apps, this could be a performance issue.

Example With useCallback

Now, we’ll use useCallback to memoize the logCount function so it only gets recreated when necessary:

import React, { useState, useCallback } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const logCount = useCallback(() => {
    console.log('Current count:', count);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={logCount}>Log Count</button>
    </div>
  );
}

export default Counter;
Enter fullscreen mode Exit fullscreen mode

How does this help?

  • The logCount function will now only be recreated when the value of count changes.
  • If count doesn’t change, the same function is used across renders, improving performance.

Key Points to Understand

  1. Memoization: useCallback remembers a function between renders.
  2. Dependency Array: The function is only recreated if one of the values in the dependency array changes.
  3. When to use: Use useCallback when:
    • You’re passing functions to child components.
    • You have expensive functions inside a component that shouldn’t be recreated unnecessarily.

A Practical Example: Parent-Child Component Interaction

If you have a child component that accepts a function as a prop, and the parent component re-renders, the child could re-render too, even if its props haven’t really changed. This is where useCallback becomes very useful.

Example without useCallback:

import React, { useState } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <Child onIncrement={increment} />
      <p>Count: {count}</p>
    </div>
  );
}

function Child({ onIncrement }) {
  console.log('Child component re-rendered!');
  return <button onClick={onIncrement}>Increment</button>;
}

export default Parent;
Enter fullscreen mode Exit fullscreen mode

Here, every time the Parent re-renders (for any reason), the increment function is recreated, causing the Child to re-render, even if the child’s onIncrement prop hasn’t changed.

Example with useCallback:

import React, { useState, useCallback } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <Child onIncrement={increment} />
      <p>Count: {count}</p>
    </div>
  );
}

function Child({ onIncrement }) {
  console.log('Child component re-rendered!');
  return <button onClick={onIncrement}>Increment</button>;
}

export default Parent;
Enter fullscreen mode Exit fullscreen mode

Now, the increment function is only recreated when count changes. So, the Child component will only re-render if the onIncrement function changes.

When Not to Use useCallback

While useCallback is useful, you don’t need to use it everywhere. It’s most beneficial when:

  • You are passing functions to child components.
  • The function does some heavy or expensive calculation.

In most simple cases, React’s default re-render behavior is efficient enough, and using useCallback everywhere can actually add complexity without much performance gain.

Conclusion

  • useCallback is a powerful tool for performance optimization in React.
  • It helps by memoizing a function and recreating it only when necessary, based on its dependencies.
  • Use it when you have expensive functions or are passing functions to child components that could lead to unnecessary re-renders.

It’s best used when you start noticing performance issues in your app due to frequent re-renders!

Some Good Resources- https://lo-victoria.com/a-look-at-react-hooks-usecallback

Top comments (0)