Skip to content

React useCallback Hook Explained with Real Examples and Best Practices

Learn what React useCallback hook does, when to use it, and how it helps prevent unnecessary re-renders in your components. Includes real examples.
3 min readReact HooksReact Js

Introduction

React's useCallback is one of those hooks that seems confusing at first — until you start dealing with unnecessary re-renders or memoized components.

In React, functions are re-created on every render. This behavior is harmless in most cases, but when passing callbacks to deeply nested or memoized child components, re-creating the function can cause unnecessary re-renders and performance issues.

This is where useCallback shines.

In this guide, you’ll learn:

  • What useCallback is
  • How it works and why it exists
  • How it compares to useMemo and inline functions
  • When to use it — and when not to
  • Real-world use cases with code examples

What is useCallback?

useCallback is a built-in React hook that lets you memoize a function — meaning it returns the same function reference between renders unless one of its dependencies has changed.

Syntax:

const memoizedFn = useCallback(() => {
  // your logic
}, [dependencies]);

If none of the dependencies change, React will return the exact same function — avoiding unnecessary function recreation and preventing extra renders in child components.

Why is this useful?

When you pass a function as a prop to a memoized child component (e.g., using React.memo), even a new function reference — with identical logic — can cause that component to re-render.

useCallback ensures that the function reference remains stable, helping with performance and preventing extra renders.

Example 1: Memoizing a Handler

import { useCallback, useState } from 'react';

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

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

  return <Child onClick={increment} />;
}

Without useCallback, the increment function would be re-created on every render, potentially triggering unnecessary renders in Child.

useCallback vs useMemo

Both useCallback and useMemo help with memoization — but they do different things.

Example 2: useCallback vs Inline Function

<Button onClick={() => doSomething()} />
const handleClick = useCallback(() => doSomething(), []);
<Button onClick={handleClick} />

In the first case, a new function is created every render. In the second case, handleClick stays the same between renders — avoiding unnecessary re-renders of Button.

Real Use Case: Avoid Re-rendering Memoized Children

const Child = React.memo(({ onClick }) => {
  console.log("Child rendered");
  return <button onClick={onClick}>Click</button>;
});

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

  const handleClick = useCallback(() => {
    console.log("Clicked");
  }, []);

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

Here, Child will NOT re-render unless handleClick changes — which it won’t, thanks to useCallback().

When to Use useCallback

  • You're passing a function as a prop to a memoized child component
  • You want to avoid unnecessary function recreation
  • You're building reusable components or component libraries
  • You’re using a stable callback in an effect

When NOT to Use useCallback

  • Your function doesn’t affect performance when recreated
  • You’re not passing it to memoized children
  • Your dependencies change frequently (useCallback won’t help much)

Note: Don’t use useCallback everywhere "just in case." It adds complexity and memory overhead. Use it when it solves a specific performance issue.

Common Mistakes with useCallback

1. Overusing it

Don’t wrap every function in useCallback by default. Only use it for actual memoization needs.

2. Missing dependencies

useCallback(() => {
  doSomething(message);
}, []); // 🚫 'message' should be in dependencies

3. Using it without memoized children

If you’re not using React.memo or passing functions to children, useCallback might have no benefit.

Best Practices for useCallback (2025)

  • Use with React.memo to prevent child re-renders
  • Use with stable dependencies to avoid re-creating the function
  • Combine with useEffect when passing a callback that must not change
  • Profile performance with dev tools before applying
  • Clean up side effects when passing memoized callbacks to useEffect

Summary: Master useCallback to Improve React Performance

useCallback is not just a "nice to have" — it’s essential when building performant React apps with lots of components or heavy rendering logic.

Remember:

  • It returns a memoized version of your function
  • It helps avoid unnecessary re-renders
  • It should be used selectively — not everywhere

If you work with React.memo, nested components, or reusable hooks — useCallback is your best friend.

Share this
Build work

Need this built properly?

I build the web and mobile products these notes come out of, in React, React Native and Node. A first conversation costs nothing and often ends with a smaller scope than you expected.