Dung (Donny) Nguyen

Senior Software Engineer

Overview of useCallback in React.js

useCallback is a React Hook that returns a memoized version of a callback function. It lets you cache a function definition between re-renders so that the same function reference is reused unless one of its dependencies changes. This is useful for optimizing performance in components that pass callbacks to child components or rely on referential equality.

Why useCallback Is Needed

In JavaScript, functions are objects. Every time a component re-renders, any function defined inside it is created anew, resulting in a different reference even if the logic is identical:

function Parent() {
  // A brand new function on every render
  const handleClick = () => console.log('clicked');
  return <Child onClick={handleClick} />;
}

If Child is wrapped in React.memo, it will still re-render every time because handleClick is a new reference each render. useCallback solves this by preserving the same function reference across renders.

How useCallback Works

Syntax

const memoizedCallback = useCallback(() => {
  // Your callback logic here
}, [dependencies]);

Basic Example

import { useState, useCallback } from 'react';

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

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

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

Because the dependency array is empty, increment keeps the same reference for the lifetime of the component.

Using useCallback with React.memo

useCallback is most valuable when passing callbacks to memoized child components:

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

const Button = memo(function Button({ onClick, children }) {
  console.log('Button rendered:', children);
  return <button onClick={onClick}>{children}</button>;
});

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

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

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

Since handleClick keeps a stable reference, the memoized Button avoids unnecessary re-renders.

useCallback vs useMemo

In fact, useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

When to Use useCallback

When Not to Use useCallback

Key Rules and Best Practices

Conclusion

useCallback helps you keep function references stable across renders, which is essential when working with memoized components, Hook dependencies, and context values. Used thoughtfully, it prevents unnecessary re-renders and improves performance, but it should be applied only where the referential stability actually matters.