useCallback Hook
Learn how useCallback in React helps memoize functions, preventing unnecessary re-creation and optimizing performance.
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.
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
.