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.
useCallback memoizes a function
reference. It does not automatically make every component faster.
Use it when function reference stability provides a real benefit.
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.
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.
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";
const functionName = useCallback(
() => {
// function code
},
[dependencies]
);
The first argument is the function. The second argument is the dependency array.
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.
const handleClick = useCallback(
() => {
console.log("Clicked");
},
[]
);
With an empty dependency array, React can reuse the same function reference across renders of that component.
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;
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.
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.
Event handler functions are a common place where
useCallback may be useful.
const handleSubmit = useCallback(
() => {
console.log("Form submitted");
},
[]
);
<button onClick={handleSubmit}>
Submit
</button>
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.
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.
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.
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>
);
}
A callback can receive parameters when it is called.
const handleUser = useCallback(
(name) => {
console.log(name);
},
[]
);
<button
onClick={() =>
handleUser("Rahul")
}
>
Select
</button>
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.
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.
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>
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.
| 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]
);
Adding useCallback everywhere is not automatically a
performance improvement.
It can be useful when:
For simple components, a normal function may be easier to understand.
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.
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.
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.
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;
Consider useCallback when:
It is not necessary for every event handler in every component.
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;
The basic workflow of useCallback is:
useCallback.Component Render
↓
useCallback()
↓
Check Dependencies
↓
┌─────┴─────┐
↓ ↓
Same Changed
↓ ↓
Reuse New
Function Function
useCallback when function reference stability is useful.useMemo when you need to memoize a calculated value.useCallback memoizes a function reference.React.memo and useCallback can work together.useCallback does not automatically prevent component rendering.useCallback returns a function, while useMemo returns a memoized value.
Question: What does the useCallback Hook
primarily memoize?