When building React applications, performance becomes important as your project grows. Sometimes a page feels slow, or components update when they do not need to. Understanding useMemo vs useCallback helps developers optimize unnecessary re-renders and improve React performance.
This often happens because React runs component functions again whenever state or props change. Every time a parent component re-renders, JavaScript creates new variables, objects, and functions. This normal behavior can cause child components to render again, even when their data has not changed.
Many React developers get confused about useMemo vs useCallback because they look very similar. Both hooks work in almost the same way, but they solve different problems.
The useMemo hook runs a function and stores its result. React will use the saved result until one of the dependencies changes.
It is useful for:
Heavy calculations
Filtered lists
Formatted data
Objects and arrays that should stay the same between renders
The useCallback hook does not run the function. Instead, it saves the function itself. It is useful when you want the same function reference across multiple renders. This helps stop child components from re-rendering when they receive functions as props.
// useMemo saves the RESULT
const cachedValue = useMemo(() => computeHeavyData(rawData), [rawData]);
// useCallback saves the FUNCTION
const cachedFunction = useCallback(() => handleUserInteraction(id), [id]);
JavaScript stores objects and functions by reference. When a component renders again, a new function or object gets a new memory reference. Even if the code inside the function stays the same, React sees it as a different object because the memory address changed. This can cause extra renders.
|
Feature |
useMemo |
useCallback |
|
Main Purpose |
Save calculated values |
Save function references |
|
What It Returns |
Value, object, or array |
Function |
|
Function Execution |
Runs immediately |
Does not run |
|
Best Use Case |
Data processing and calculations |
Event handlers |
|
Common Usage |
Filtering, sorting, formatting |
Button clicks and callbacks |
|
Common Mistake |
Using it for simple calculations |
Missing dependencies |
Many developers try to use these hooks everywhere. This is not a good idea. Every cached value or function needs memory. React must also compare dependency arrays during each render. Using these hooks too much can actually reduce performance. For effective React performance optimization, use them only when they solve a real problem.
Use useMemo when a calculation takes time or creates large data structures.
Expensive Calculations
If your code works with large lists, complex calculations, or heavy data processing, useMemo can help avoid repeating the work.
Examples include:
Sorting large datasets
Searching through many records
Complex mathematical operations
Date processing
Data Transformation
Sometimes data from an API needs to be changed before display.
Examples include:
Filtering data
Formatting values
Grouping records
Creating display-ready lists
Stable Object References
Objects created inside components get recreated on every render. useMemo can keep those object references stable when needed.
Use useCallback when function references need to stay the same.
Passing Functions to Child Components
A common use case is passing event handlers to components wrapped with React.memo(). Without useCallback, the child component may render again because it receives a new function reference.
Returning Functions From Custom Hooks
Custom hooks often return functions. useCallback helps make sure these returned functions stay stable for other components.
Event Listeners and Effects
Functions used inside useEffect dependencies often benefit from useCallback. This helps avoid unnecessary effect executions.
Many projects use different styling methods, such as:
Inline styles
Styled Components
Dynamic themes
When working with CSS in React, style objects can sometimes create performance issues.
Consider this example:
const dynamicSidebarStyle = {
backgroundColor: theme === 'dark' ? '#1a1a1a' : '#ffffff',
padding: '24px',
display: 'flex'
};
This object is created again during every render.
Because it gets a new reference each time, components that use it may render more often than needed.
For better React performance optimization, you can use useMemo.
const sidebarStyles = useMemo(() => ({
backgroundColor: theme === 'dark' ? '#1a1a1a' : '#ffffff',
padding: '24px',
display: 'flex'
}), [theme]);
Now React only creates a new style object when the theme changes. This approach can help improve performance when working with dynamic CSS in React layouts.
A real example makes these hooks easier to understand.
Imagine an inventory dashboard that lets users search products and switch between light and dark mode.
import React, { useState } from 'react';
const filterInventoryItems = (items, query) => {
console.log('Running heavy filtering logic...');
return items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
};
export const InventoryDashboard = ({ rawItems }) => {
const [searchQuery, setSearchQuery] = useState('');
const [isDarkMode, setIsDarkMode] = useState(false);
const visibleItems = filterInventoryItems(rawItems, searchQuery);
const logSelection = (itemId) => {
console.log(`Selected item ${itemId}`);
};
return (
<div>
<AssetList
items={visibleItems}
onItemClick={logSelection}
/>
</div>
);
};
When dark mode changes:
The filtering function runs again.
A new click handler function is created.
The child component receives new props.
The child component renders again.
All of this happens even when the inventory data has not changed.
Now let's improve the component.
import React, { useState, useMemo, useCallback } from 'react';
export const OptimizedInventoryDashboard = ({ rawItems }) => {
const [searchQuery, setSearchQuery] = useState('');
const [isDarkMode, setIsDarkMode] = useState(false);
const visibleItems = useMemo(() => {
return filterInventoryItems(rawItems, searchQuery);
}, [rawItems, searchQuery]);
const logSelection = useCallback((itemId) => {
console.log(`Selected item ${itemId}`);
}, []);
return (
<div>
<AssetList
items={visibleItems}
onItemClick={logSelection}
/>
</div>
);
};
The filtering result is now saved with useMemo.
The click handler is now saved with useCallback.
When dark mode changes:
The filter does not run again.
The click handler stays the same.
The child component can skip extra renders.
This is a practical example of React efficiency hooks improving performance.

