React useCallback Explained in Detail

React useCallback Explained in Detail

Learn how the React useCallback hook helps optimize performance by memoizing functions. Understand when to use it, when not to use it, and why blindly optimizing can harm performance. Includes examples for dependency tracking and preventing unnecessary re-renders.

frontend
August 31, 2025
7 min read

React useCallback Explained in Detail

useCallback is a React hook that helps React remember a function between re-renders. Without it, React recreates a new function every time a component renders, which can cause unnecessary re-renders or performance issues.

Why Do We Need `useCallback`?

Every time a component re-renders, all functions inside it are recreated. Even if the code is the same, the function reference changes. This causes two main problems:

  1. Child components may re-render unnecessarily when passed a new function prop.
  2. Expensive functions get recreated and run too often.

Basic Example

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

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

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

With Dependencies

If your function depends on props or state, add them to the dependency array. This way, React only recreates the function when those dependencies change.

js
function Greeting({ name }) {
  const [greetCount, setGreetCount] = useState(0);

  const greet = useCallback(() => {
    console.log(`Hello, ${name}`);
    setGreetCount(c => c + 1);
  }, [name]);

  return (
    <div>
      <p>Greetings sent: {greetCount}</p>
      <button onClick={greet}>Say Hello</button>
    </div>
  );
}

With Expensive Functions and React.memo

When you pass a function to a memoized child component, React will avoid re-rendering the child unless the function actually changes. This is where useCallback is very useful.

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

function ExpensiveChild({ compute }) {
  console.log('Child re-rendered');
  return <div>Result: {compute()}</div>;
}

const MemoizedChild = React.memo(ExpensiveChild);

function App() {
  const [value, setValue] = useState(0);

  const expensiveFunction = useCallback(() => {
    console.log('Running expensive calculation...');
    return value * 1000;
  }, [value]);

  return (
    <div>
      <button onClick={() => setValue(v => v + 1)}>Increase</button>
      <MemoizedChild compute={expensiveFunction} />
    </div>
  );
}

When to Use `useCallback`?

  • When passing a function to a memoized child (React.memo).
  • When your function does heavy calculations.
  • When you notice unnecessary re-renders.

When Not to Use

  • For small, simple functions.
  • If there are no performance problems.
  • Just for the sake of using it (blind optimization).

Summary

useCallback memoizes a function so that it only changes when dependencies change. It helps prevent unnecessary re-renders and should be used wisely for performance optimization.