- Published on
When and How to Use useMemo and useCallback: A Practical Guide
- Authors
- Name
- Mohit Verma
Hey there! 👋 If you've been working with React, you've probably heard about useMemo
and useCallback
. But when should you actually use them? Let's break it down in simple terms with some real-world examples.
The Golden Rule First!
Before we dive in, remember this: Don't optimize prematurely! Both useMemo
and useCallback
are optimization hooks. You don't need them everywhere. In fact, incorrect usage can make your app slower.
When to Use useMemo
Use useMemo
when you have:
- Expensive calculations
- Complex object creation that triggers re-renders
- Values used in dependency arrays of other hooks
Let's look at some examples:
// 🚫 Don't do this - simple calculation
const total = useMemo(() => price + tax, [price, tax]);
// ✅ Do this - expensive calculation
const expensiveValue = useMemo(() => {
return someArray.reduce((acc, item) => {
// Complex calculations here
return acc + complexOperation(item);
}, 0);
}, [someArray]);
// ✅ Do this - object used in deps
const memoizedObject = useMemo(() => ({
id: props.id,
name: props.name
}), [props.id, props.name]);
useEffect(() => {
// This effect won't re-run unnecessarily
doSomething();
}, [memoizedObject]);
Smart useCallback Usage
useCallback
is your friend when:
- Passing callbacks to optimized child components
- Callbacks are used in dependency arrays
- Preserving function references is important
Here's how to use it effectively:
// ✅ Good use case - callback passed to optimized component
const MemoizedChild = memo(({ onClick }) => {
console.log("Child rendered");
return <button onClick={onClick}>Click me</button>;
});
function Parent() {
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []); // No dependencies needed here
return <MemoizedChild onClick={handleClick} />;
}
// ✅ Good use case - callback in dependency array
function DataFetcher() {
const [data, setData] = useState(null);
const fetchData = useCallback(async () => {
const response = await fetch('/api/data');
const json = await response.json();
setData(json);
}, []); // Empty deps as it doesn't depend on props/state
useEffect(() => {
fetchData();
}, [fetchData]); // Used in deps array
return <div>{/* render data */}</div>;
}
Common Mistakes to Avoid
- Over-optimization
// 🚫 Don't do this
function SimpleComponent() {
// Unnecessary memoization
const handleClick = useCallback(() => {
console.log('clicked');
}, []);
return <button onClick={handleClick}>Click me</button>;
}
// ✅ Do this instead
function SimpleComponent() {
const handleClick = () => console.log('clicked');
return <button onClick={handleClick}>Click me</button>;
}
- Missing Dependencies
// 🚫 Don't do this
const memoizedValue = useMemo(() => {
return data.filter(item => item.id === selectedId);
}, []); // Missing dependencies!
// ✅ Do this instead
const memoizedValue = useMemo(() => {
return data.filter(item => item.id === selectedId);
}, [data, selectedId]); // Include all dependencies
Real-World Examples
1. Data Grid with Sorting
function DataGrid({ data, sortConfig }) {
// Memoize expensive sorting operation
const sortedData = useMemo(() => {
console.log('Sorting data...'); // You'll see this less often
return [...data].sort((a, b) => {
if (sortConfig.direction === 'asc') {
return a[sortConfig.key] > b[sortConfig.key] ? 1 : -1;
}
return a[sortConfig.key] < b[sortConfig.key] ? 1 : -1;
});
}, [data, sortConfig.key, sortConfig.direction]);
return (
<table>
<tbody>
{sortedData.map(item => (
<tr key={item.id}>{/* render row */}</tr>
))}
</tbody>
</table>
);
}
2. Search Component with Debounce
function SearchComponent({ onSearch }) {
// Memoize debounced function
const debouncedSearch = useCallback(
debounce((term) => {
onSearch(term);
}, 300),
[onSearch]
);
return (
<input
type="text"
onChange={e => debouncedSearch(e.target.value)}
placeholder="Search..."
/>
);
}
Performance Testing Tip
Want to know if your memoization is helping? Use React DevTools:
- Open React DevTools
- Go to Profiler
- Record interactions
- Look for unnecessary re-renders
When to Skip Memoization
Skip these hooks when:
- The computation is simple
- The component always needs to re-render anyway
- The props/state change frequently
// 🚫 Don't memoize simple calculations
const doubledValue = useMemo(() => number * 2, [number]);
// ✅ Just do the calculation
const doubledValue = number * 2;
Conclusion
Remember:
- Profile first, optimize later
- Use these hooks when you have measurable performance issues
- Always include proper dependencies
- Test performance impact before and after
Happy coding! And remember, the best optimization is often the one you don't need to make! 😉