React useCallback
Optimizing function references in React
⚡ What is useCallback?
useCallback is a React Hook that returns a memoized callback function. It prevents unnecessary re-creation of functions, improving performance when passing callbacks to optimized child components.
import { useCallback } from 'react';
function MyComponent() {
const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);
return <Button onClick={handleClick} />;
}
useCallback Concepts
Memoization
Caches function between re-renders
const memoizedFn = useCallback(fn, deps);
Dependencies
Array of values function depends on
useCallback(fn, [count, name]);
Performance
Prevents unnecessary child re-renders
<Child onClick={memoizedFn} />
Referential Equality
Same function reference across renders
// Same reference if deps unchanged
🔹 Basic useCallback Example
Without useCallback, functions are recreated on every render. This example shows how useCallback preserves function identity between renders when dependencies don't change.
import { useState, useCallback } from 'react';
function SearchBar() {
const [query, setQuery] = useState('');
// This function is recreated only when query changes
const handleSearch = useCallback(() => {
console.log('Searching for:', query);
// Perform search logic here
}, [query]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<button onClick={handleSearch}>Search</button>
</div>
);
}
Output:
🔹 Preventing Child Re-renders
useCallback is most useful with React.memo. When you pass a callback to a memoized child component, useCallback ensures the child doesn't re-render unnecessarily.
import { useState, useCallback, memo } from 'react';
// Memoized child component
const Button = memo(({ onClick, children }) => {
console.log('Button rendered');
return <button onClick={onClick}>{children}</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// Without useCallback, Button re-renders when text changes
const increment = useCallback(() => {
setCount(c => c + 1);
}, []); // No dependencies - function never changes
return (
<div>
<p>Count: {count}</p>
<Button onClick={increment}>Increment</Button>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Type here..."
/>
<p>Button won't re-render when typing!</p>
</div>
);
}
Output:
Count: 0
Button won't re-render when typing!
🔹 useCallback with Dependencies
When your callback uses props or state, include them in the dependency array. The function will be recreated only when these dependencies change.
import { useState, useCallback } from 'react';
function ItemList() {
const [items, setItems] = useState(['Apple', 'Banana']);
const [filter, setFilter] = useState('');
// Recreated only when filter changes
const filterItems = useCallback(() => {
return items.filter(item =>
item.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]); // Dependencies: items and filter
const filteredItems = filterItems();
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter items..."
/>
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
🔹 When to Use useCallback
useCallback is not always necessary. Use it strategically for performance optimization:
✅ Use useCallback when:
- Passing to memoized components: Child uses React.memo
- Dependency in other hooks: Function used in useEffect, useMemo
- Expensive child components: Preventing unnecessary re-renders matters
- Event handlers in lists: Many items with individual handlers
❌ Don't use useCallback when:
- Simple components: No performance issues observed
- Not passed as props: Function only used locally
- Premature optimization: No measurable benefit
- Every function: Adds complexity without need
🔹 useCallback vs Regular Function
Compare the difference between regular functions and useCallback:
import { useState, useCallback } from 'react';
function Comparison() {
const [count, setCount] = useState(0);
// ❌ Recreated on every render
const regularFunction = () => {
console.log('Regular function');
};
// ✅ Recreated only when count changes
const memoizedFunction = useCallback(() => {
console.log('Memoized function', count);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={regularFunction}>Regular</button>
<button onClick={memoizedFunction}>Memoized</button>
</div>
);
}