ReviseAlgo Logo

Hooks

useCallback

Master the useCallback Hook, covering function definition caching, reference equality, child re-render optimizations, and comparisons with useMemo.

Last Updated: July 15, 2026 12 min read

1. Learning Objectives

In this lesson, you will learn how to optimize rendering performance by caching function definitions. By the end of this topic, you will be able to:

  • Differentiate between caching a function's return value (useMemo) and the function definition itself (useCallback).
  • Apply the useCallback Hook to keep function references stable between renders.
  • Prevent unnecessary re-renders of child components wrapped in React.memo.
  • Configure correct dependency arrays to prevent stale closures in callbacks.
  • Identify when wrapping callbacks adds unnecessary overhead.

2. Overview

The useCallback Hook is a React optimization tool that caches a function definition between renders. In JavaScript, functions are objects, and declaring a function inside a component creates a new reference on every render. useCallback keeps this reference stable, preventing child components that receive the function as a prop from re-rendering unnecessarily.

3. Why This Topic Matters

Stable function references are crucial for child component optimizations:

  • Downstream Re-renders: If you pass an inline function (e.g. onClick={() => handleClick(id)}) to a child component, the child will re-render on every parent render, even if it is wrapped in React.memo, because the function reference is always new.
  • Dependency Loops: If a callback function is read inside a useEffect Hook in a child component, passing a new function reference on every render causes the effect to run repeatedly, potentially creating infinite loops.

4. Real-World Analogy

Think of useCallback like **a company distributing business cards**:

  • Without useCallback: Reprinting your business cards (recreating the function) on every single client meeting, even if your phone number and email address did not change. This wastes paper and money.
  • With useCallback: Reusing the same stack of printed cards (the cached function reference) across meetings. You only reprint the cards if your contact details change (dependencies update).

5. Core Concepts

Below is a comparison of the useMemo and useCallback Hooks:

Feature useMemo useCallback
What it caches The *return value* of a calculation function. The *function definition itself* (without executing it).
Primary Use Case Caching expensive calculations (sorting, filtering). Caching event handlers passed to memoized child components.
Equivalence useMemo(() => fn, [deps]) Equivalent to: useMemo(() => () => fn(), [deps])

6. Syntax & API Reference

This example shows how to use `useCallback` to keep a function reference stable:

7. Visual Diagram

This diagram displays how stable callback references prevent optimized child components from re-rendering:

8. Live Example — Full Working Code

Below is a complete HTML page demonstrating child re-render prevention using `useCallback` and `React.memo`:

What just happened? Opening the console shows that clicking "Toggle" does not trigger any re-renders for the ListItem components. This is because handleDelete is wrapped in useCallback, ensuring its reference remains identical between parent renders. When combined with React.memo, this allows the child components to skip re-rendering.

9. Interactive Playground

Try It Yourself Challenges:

  1. Remove useCallback from the handleDelete declaration (declaring it as a standard function) and verify that clicking the toggle button now triggers child re-renders.
  2. Add a new input text field, and verify that the memoized children skip re-renders while the user is typing.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Wrapping inline callbacks passed to standard elements Wrapping functions passed to built-in HTML tags (like <button>) in useCallback is redundant. Standard DOM tags do not benefit from reference checks, so this wrapper simply adds memory overhead. <button onClick={useCallback(fn, [])}> <button onClick={fn}> (pass directly)
Creating stale closures by omitting dependencies If you read variables (like state or props) inside a callback but omit them from the dependency array, the Hook will continue to return a cached function that reference old values, causing bugs. useCallback(() => console.log(val), []) (val is omitted) useCallback(() => console.log(val), [val])

11. Best Practices

  • Only memoize when necessary: Only use `useCallback` when passing functions to optimized child components wrapped in `React.memo`, or when the function is a dependency of another Hook (like `useEffect`).
  • Always match dependencies: List all variables referenced inside the callback in its dependency array to prevent stale closures.
  • Use functional state updates: If your callback only updates state based on its previous value, use the updater function syntax (`setCount(prev => prev + 1)`) to avoid having to list the state variable as a dependency, keeping the function reference stable.

12. Browser Compatibility/Requirements

The useCallback Hook is supported in all browsers that run React.

13. Interview Questions

Q1: What is the main difference between `useMemo` and `useCallback`?

Answer: Both are optimization Hooks, but they cache different things: (1) `useMemo` executes the function and caches its **return value** (like a sorted array), whereas (2) `useCallback` caches the **function definition itself** without executing it, keeping the function reference stable across renders.

Q2: Why does wrapping a parent component function in `useCallback` have no effect if the child component is not wrapped in `React.memo`?

Answer: By default, React re-renders all child components whenever a parent component renders, regardless of whether their props have changed. If the child is not optimized with `React.memo`, it will re-render anyway. `useCallback` only improves performance when the child uses reference checks (like `React.memo`) to skip rendering when props are identical.

14. Debugging Exercise

Identify why this input search handler returns incorrect filtering results on keystrokes:

View Solution

Diagnosis: The dependency array is empty, causing `useCallback` to cache a function definition that references the initial empty `query` value (`''`). Because of this stale closure, subsequent calls to the function continue to send empty search parameters to the API.

Fix: Add query to the dependency array:

15. Practice Exercises

Exercise 1: Create a button list panel

Build a component named ActionGrid. Declare callback handlers for buttons using useCallback, passing them to memoized button elements to prevent child re-renders on parent state changes.

16. Scenario-Based Challenge

The High-Frequency Event Debounce Callback:

You are designing a window scroll logger that sends event metrics to a server. To prevent overload, you decide to debounce the scroll handler. Explain how you would use useCallback to keep the debounced function reference stable, preventing the scroll handler from resetting on every render.

17. Quick Quiz

Q1: Which API is used to optimize child functional components by skipping rendering when incoming props are identical?

A) React.useMemo

B) React.memo

C) React.useCallback

Answer: B — React.memo is a higher-order component used to optimize functional components by skipping rendering if props are identical.

18. Summary & Key Takeaways

  • The useCallback Hook caches function definitions between renders.
  • Use `useCallback` to prevent child components optimized with `React.memo` from re-rendering due to changing function references.
  • Ensure all state, props, or variables referenced inside the callback are listed in the dependency array.
  • Use functional updates to keep callback function references stable over time.

19. Cheat Sheet

useCallback Pattern Purpose
const fn = useCallback(() => { ... }, [dep]) Caches a function definition, updating it only when the dependency values change.
const fn = useCallback(() => { ... }, []) Caches the function definition permanently (stable reference).
const Child = React.memo(MyComponent); Optimizes a child component by skipping rendering if props are identical.
---