Lesson 35 of 60 – useCallback Hook
58%

useCallback Hook

The useCallback Hook is used to memoize a function reference. It can help prevent unnecessary function recreation when a component re-renders, especially when passing callbacks to optimized child components.

Note: useCallback memoizes a function reference. It does not automatically make every component faster. Use it when function reference stability provides a real benefit.

1. What is useCallback?

useCallback is a React Hook used to memoize a function.

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

React can reuse the same function reference when the dependencies have not changed.

2. Why use useCallback?

Functions created inside a component can receive a new reference when the component renders again.

function App() {

    const handleClick = () => {
        console.log("Clicked");
    };

}

useCallback can preserve the function reference when its dependencies remain unchanged.

3. Importing useCallback

Import useCallback from React before using it.

import { useCallback } from "react";

It can also be imported together with other Hooks.

import {
    useCallback,
    useState
} from "react";

4. Basic useCallback Syntax

const functionName = useCallback(
    () => {
        // function code
    },
    [dependencies]
);

The first argument is the function. The second argument is the dependency array.

5. Function Reference

In JavaScript, two separately created functions are different object references even if they contain the same code.

const first = () => {
    console.log("Hello");
};

const second = () => {
    console.log("Hello");
};

The two function references are different.

6. useCallback Preserves Function Reference

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

With an empty dependency array, React can reuse the same function reference across renders of that component.

7. Simple useCallback Example

import {
    useCallback,
    useState
} from "react";

function App() {

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

    const handleClick =
        useCallback(() => {

            console.log("Button clicked");

        }, []);

    return (
        <div>

            <h2>
                Count: {count}
            </h2>

            <button
                onClick={handleClick}
            >
                Click
            </button>

            <button
                onClick={() =>
                    setCount(count + 1)
                }
            >
                Increase
            </button>

        </div>
    );
}

export default App;

8. useCallback with Dependencies

A callback can depend on state or props.

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

When count changes, React creates a new callback reference that uses the updated value.

9. Dependency Array

The dependency array tells React when the memoized function needs to be recreated.

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

If name changes, the callback is updated.

10. useCallback with Event Handlers

Event handler functions are a common place where useCallback may be useful.

const handleSubmit = useCallback(
    () => {
        console.log("Form submitted");
    },
    []
);
<button onClick={handleSubmit}>
    Submit
</button>

11. useCallback with Child Components

One important use case is passing a callback to a child component.

function Parent() {

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

    return (
        <Child
            onClick={handleClick}
        />
    );
}

The child receives a stable function reference while the dependencies remain unchanged.

12. React.memo and useCallback

useCallback is commonly discussed together with React.memo.

const Child = React.memo(
    function Child({ onClick }) {

        return (
            <button onClick={onClick}>
                Click
            </button>
        );

    }
);

A stable callback can help a memoized child avoid re-rendering caused only by a changed function reference.

13. Why React.memo Matters

React.memo can skip a child component's render when its props have not changed according to its comparison.

const Child = React.memo(
    function Child({ name }) {

        return (
            <h2>
                {name}
            </h2>
        );

    }
);

If a callback prop is recreated on every parent render, it can appear to have changed even when its behavior is the same.

14. Parent and Child Example

function Parent() {

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

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

    return (
        <div>

            <h2>
                Count: {count}
            </h2>

            <button
                onClick={() =>
                    setCount(count + 1)
                }
            >
                Increase
            </button>

            <Child
                onClick={handleClick}
            />

        </div>
    );
}

15. Passing Parameters to a Callback

A callback can receive parameters when it is called.

const handleUser = useCallback(
    (name) => {
        console.log(name);
    },
    []
);
<button
    onClick={() =>
        handleUser("Rahul")
    }
>
    Select
</button>

16. useCallback with Props

A callback can use values received through props.

function Parent({ userId }) {

    const handleUser = useCallback(
        () => {
            console.log(userId);
        },
        [userId]
    );

    return (
        <Child
            onUser={handleUser}
        />
    );
}

Because the callback uses userId, it belongs in the dependency array.

17. useCallback with useState

function Counter() {

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

    const increase =
        useCallback(() => {

            setCount(
                previous =>
                    previous + 1
            );

        }, []);

    return (
        <button onClick={increase}>
            {count}
        </button>
    );
}

The functional state update allows the callback to update the latest state without needing count as a dependency in this example.

18. useCallback with Forms

A callback can be used for form submission or field actions.

const handleSubmit =
    useCallback((event) => {

        event.preventDefault();

        console.log("Submitted");

    }, []);
<form onSubmit={handleSubmit}>

    <input
        type="text"
        placeholder="Name"
    />

    <button type="submit">
        Submit
    </button>

</form>

19. useCallback with Lists

When rendering a list of memoized child components, stable callbacks can sometimes help reduce unnecessary child renders.

const handleSelect =
    useCallback((id) => {

        console.log("Selected:", id);

    }, []);

The callback can then be passed to list items.

20. useCallback vs useMemo

useCallback useMemo
Memoizes a function reference. Memoizes the result of a calculation.
Returns a function. Returns a calculated value.
Useful for callback props. Useful for expensive calculations.
const handleClick = useCallback(
    () => doSomething(),
    [data]
);

const value = useMemo(
    () => calculate(data),
    [data]
);

21. useCallback Does Not Automatically Improve Performance

Adding useCallback everywhere is not automatically a performance improvement.

It can be useful when:

  • A callback is passed to a memoized child.
  • Function reference stability matters.
  • A callback is used as a dependency in another Hook.

For simple components, a normal function may be easier to understand.

22. useCallback with useEffect

A function used as a dependency of useEffect can cause the effect to run again if the function reference changes on every render.

const fetchData = useCallback(
    () => {
        console.log("Fetching data");
    },
    [userId]
);

useEffect(() => {

    fetchData();

}, [fetchData]);

Here, the callback remains stable until userId changes.

23. Empty Dependency Array

An empty dependency array means the callback does not depend on changing values from the component.

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

React can reuse the same callback reference across renders.

24. Multiple Dependencies

A callback can depend on multiple values.

const handleUser = useCallback(
    () => {

        console.log(
            name,
            userId,
            role
        );

    },
    [name, userId, role]
);

If any dependency changes, React can create an updated callback.

25. Common useCallback Mistakes

  • Using useCallback for every function.
  • Forgetting dependencies.
  • Adding incorrect dependencies.
  • Expecting useCallback to prevent component rendering.
  • Using it without a performance-related reason.
  • Confusing useCallback with useMemo.
  • Passing a memoized callback to a child that is not affected by its reference.

26. useCallback with React.memo Example

import {
    useCallback,
    useState
} from "react";

const Button = React.memo(
    function Button({ onClick }) {

        console.log("Button rendered");

        return (
            <button onClick={onClick}>
                Child Button
            </button>
        );

    }
);

function App() {

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

    const handleClick =
        useCallback(() => {

            console.log("Clicked");

        }, []);

    return (
        <div>

            <h2>
                Count: {count}
            </h2>

            <button
                onClick={() =>
                    setCount(count + 1)
                }
            >
                Increase
            </button>

            <Button
                onClick={handleClick}
            />

        </div>
    );
}

export default App;

27. When Should You Use useCallback?

Consider useCallback when:

  • A function is passed to a memoized child component.
  • A stable function reference is important.
  • A function is used as a dependency of another Hook.
  • Profiling shows unnecessary work related to callback identity.

It is not necessary for every event handler in every component.

28. Complete useCallback Example

import {
    useCallback,
    useState
} from "react";

const Child = React.memo(
    function Child({ onSelect }) {

        return (
            <button
                onClick={() =>
                    onSelect(10)
                }
            >
                Select Student
            </button>
        );

    }
);

function App() {

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

    const handleSelect =
        useCallback((id) => {

            console.log(
                "Student ID:",
                id
            );

        }, []);

    return (
        <div>

            <h2>
                Count: {count}
            </h2>

            <button
                onClick={() =>
                    setCount(count + 1)
                }
            >
                Increase
            </button>

            <Child
                onSelect={handleSelect}
            />

        </div>
    );
}

export default App;

29. useCallback Workflow

The basic workflow of useCallback is:

  1. Identify a callback whose reference stability matters.
  2. Wrap the function with useCallback.
  3. Add the values used by the callback to the dependency array.
  4. Pass the callback where it is needed.
  5. React can reuse the callback when dependencies remain unchanged.
  6. When a dependency changes, an updated callback can be created.
Component Render
       ↓
 useCallback()
       ↓
Check Dependencies
       ↓
 ┌─────┴─────┐
 ↓           ↓
Same       Changed
 ↓           ↓
Reuse      New
Function  Function

30. useCallback Best Practices

  • Use useCallback when function reference stability is useful.
  • It is especially useful with memoized child components.
  • Keep the dependency array accurate.
  • Do not use it for every function automatically.
  • Use functional state updates when appropriate.
  • Remember that useCallback memoizes a function reference.
  • Use useMemo when you need to memoize a calculated value.
  • Keep callback logic simple and readable.
  • Consider actual performance needs before adding memoization.
  • Use profiling and measurement when optimizing larger applications.

📌 Key Points

  • useCallback memoizes a function reference.
  • It accepts a callback function and dependency array.
  • The callback can be reused when its dependencies do not change.
  • It is commonly used when passing functions to memoized child components.
  • React.memo and useCallback can work together.
  • Callbacks can depend on state and props.
  • Dependencies should accurately represent values used by the callback.
  • useCallback does not automatically prevent component rendering.
  • useCallback returns a function, while useMemo returns a memoized value.
  • Use it when function reference stability provides a meaningful benefit.

🧠 Quick Quiz

Question: What does the useCallback Hook primarily memoize?