In React applications, unnecessary function re-creation can lead to performance issues, especially when passing functions as props to child components. This is where the useCallback
in React comes in.
In this blog, you’ll learn about the useCallback
hook, including its use case and syntax with examples, and the key difference between the useCallback
and useMemo
hooks.
Before we get started, don’t forget to subscribe to my newsletter!
Get the latest tips, tools, and resources to level up your web development skills delivered straight to your inbox. Subscribe here!
Now, let’s jump right into it!
What is useCallback?
useCallback
is a React Hook that memoizes functions. This stores the reference of the function so that you can avoid creating a new function on each render.
This improves the performance, especially when you need to pass functions from parent components to child components.
If a function is creating unnecessary, then it’s a best practice to use the useCallback
hook.
Basic Syntax of useCallback
Before using useCallback
, you need to import it from React:
import { useCallback } from "react";
Syntax:
const memoizedFunction = useCallback(() => {
// Function logic
}, [dependencies]);
Here,
-
useCallback
accepts a function that gets memoized. - The function will be re-created only when the dependencies change.
- If dependencies are the same, then the same function reference will be returned.
Function Re-Creation Without useCallback
If you don’t use the useCallback
hook, then on each render, a new function will be created, which causes unnecessary renders.
import { useState } from "react";
function ChildComponent({ handleClick }) {
console.log("Child re-rendered!"); // The child component re-render on each render
return <button onClick={handleClick}>Click Me</button>;
}
export default function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
console.log("Button clicked!");
};
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent handleClick={handleClick} />
</div>
);
}
How does it work?
Child Component (ChildComponent):
- Accepts
handleClick
as a prop. - Logs “Child re-rendered!” on every render.
- Renders a button that calls
handleClick
when clicked.
Parent Component (App):
- Uses
useState
to manage count. - Defines the
handleClick
function (logs “Button clicked!”). - Renders:
- A paragraph showing count.
- A button to increment the count.
- ChildComponent, passing
handleClick
as a prop.
Re-render Issue:
- On every App re-render (when count updates),
handleClick
is recreated. - Since a new function reference is passed, ChildComponent also re-renders unnecessarily.
Function Re-Creation Avoided With useCallback
You can avoid the unnecessary function re-creation using useCallback
.
import { useState, useCallback } from "react";
function ChildComponent({ handleClick }) {
console.log("Child re-rendered!"); // This will only re-render when function changes
return <button onClick={handleClick}>Click Me</button>;
}
export default function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Button clicked!");
}, []); // Function reference will not change
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<ChildComponent handleClick={handleClick} />
</div>
);
}
How does it work?
Child Component (ChildComponent):
- Accepts
handleClick
as a prop. - Logs “Child re-rendered!” on render.
- Renders a button that calls
handleClick
when clicked.
Parent Component (App):
- Uses
useState
to manage count. - Wraps
handleClick
withuseCallback
(dependency array[ ]
ensures function reference remains stable). - Renders:
- A paragraph showing count.
- A button to increment the count.
- ChildComponent, passing the memoized
handleClick
.
Optimization with useCallback
:
- Now, the
handleClick
reference does not change on re-renders. - ChildComponent only re-renders when necessary, reducing unnecessary renders.
Event Handlers in useCallback
If an event handler is repeatedly re-created, then you can optimize this by using the useCallback
.
Without useCallback (Unoptimized Code)
import { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1); // New function is creating on each render
return <button onClick={increment}>Count: {count}</button>;
}
How does it work?
Defines increment Function:
- Increments count by 1.
- Created inside the component, so a new function is created on every render.
Returns a Button:
- Displays count.
- Calls increment on click.
Issue: Unnecessary function re-creation
- On each re-render, a new instance of increment is created.
- This can cause performance issues if passed as a prop to child components.
With useCallback (Optimized Code)
import { useState, useCallback } from "react";
export default function App() {
const [count, setCount] = useState(0);
const increment = useCallback(() => setCount(count + 1), [count]); // Memoized function
return <button onClick={increment}>Count: {count}</button>;
}
How does it work?
Defines increment Function:
- Wrapped in
useCallback
with[count]
as a dependency. - Memoizes increment, so a new function is created only when
count
changes.
Returns a Button:
- Displays count.
- Calls increment on click.
Why is this better than the previous version?
- Prevents unnecessary function re-creation on each render.
- Useful when passing
increment
as a prop to child components.
useCallback in API Calls & Dependencies
If a function calls an API or depends on a state, then you can optimize that by using the useCallback
.
import { useState, useCallback } from "react";
export default function App() {
const [query, setQuery] = useState("");
const fetchData = useCallback(() => {
console.log("Fetching data for:", query);
// API call logic
}, [query]); // Function will recreate only when the `query` changes
return (
<div>
<input onChange={(e) => setQuery(e.target.value)} />
<button onClick={fetchData}>Search</button>
</div>
);
}
How does it work?
Defines fetchData
Function:
- Logs “Fetching data for:” along with the current query.
- Wrapped in
useCallback
with[query]
as a dependency. - This ensures that
fetchData
is only recreated when the query changes.
Returns an Input Field & Button:
- The input updates the query on every change.
- The button triggers fetchData to “fetch data” for the current query.
Optimization Benefit:
- Prevents unnecessary re-creation of fetchData unless query changes.
- Useful when passing fetchData to child components to avoid unnecessary re-renders.
What is the difference between useCallback and useMemo?
Feature | useCallback | useMemo |
---|---|---|
Purpose | Function memoization | Value memoization |
Returns | Memoized function reference | Memoized computed value |
Use Case | When unnecessary function recreation happening | When expensive calculation is happening repeatedly |
In simple:
- If you need to memoize a function, use
useCallback
. - If you need to memoize a computed value or expensive calculation, use
useMemo
.
🎯Wrapping Up
That’s all for today!
For paid collaboration connect with me at : [email protected]
I hope this post helps you.
If you found this post helpful, here’s how you can support my work:
☕ Buy me a coffee – Every little contribution keeps me motivated!
📩 Subscribe to my newsletter – Get the latest tech tips, tools & resources.
𝕏 Follow me on X (Twitter) – I share daily web development tips & insights.
Keep coding & happy learning!
Top comments (7)
Brilliantly explained, Shefali! 🙌 Your article on useCallback dives deep into one of the most essential React hooks with such clarity. Memoization can be tricky for many, but your real-world examples and practical insights make it so much easier to understand. Definitely bookmarking this one! 🔥✨
So happy to know that! Thank you so much, Robin🙏
Welcome
I've been following your articles and I can't be grateful enough. Each of them helps me to refresh my practice on React
That means a lot! Thank you so very much🙏
Really clear breakdown, especially how you showed the child component re-render case - I've run into that way too often. Have you ever had a moment where useCallback didn't fix the extra renders and you had to dig deeper?
pretty cool seeing this laid out with examples. you think people sometimes overuse memoization hooks or nah?
Some comments may only be visible to logged-in visitors. Sign in to view all comments.