Introduction
As your React application grows, you may notice it slowing down or lagging, especially when components re-render more often than necessary. React re-renders components whenever their state or props change — but sometimes, unnecessary re-renders can hurt performance. This is where memo, useMemo, and useCallback come in. These tools help you control re-renders, avoid heavy calculations on each render, and prevent functions from being recreated unnecessarily.
Why Do React Apps Become Slow?
React becomes slow when components re-render too many times. This usually happens because:
Props or state change frequently
Functions are recreated on every render.
Heavy calculations run repeatedly.
Large lists render without optimization.
Child components re-render even when data has not changed
Using memo, useMemo, and useCallback helps fix these issues and makes your app run more smoothly.
1. React.memo — Prevents Unnecessary Re-renders of Components
React.memo is used to stop a component from re-rendering if its props haven’t changed.
How It Works
React normally re-renders child components whenever the parent renders. But with memo, React compares old and new props — if they are the same, it skips the render.
Example Without memo
function Child({ count }) {
console.log("Child rendered");
return <h2>Count: {count}</h2>;
}
function App() {
const [value, setValue] = useState(0);
return (
<>
<button onClick={() => setValue(value + 1)}>Increase</button>
<Child count={10} />
</>
);
}
Here, Child re-renders every time App re-renders, even though count never changes.
Fix Using memo
const Child = React.memo(function Child({ count }) {
console.log("Child rendered");
return <h2>Count: {count}</h2>;
});
Now Child renders only once, unless its props change.
When to Use memo
When child components receive the same props repeatedly
When rendering large UI blocks
When components re-render without any real change
When Not to Use memo
2. useMemo — Caches Expensive Calculations
useMemo is used when you have a slow or heavy calculation that you do not want to repeat on every render.
How It Works
useMemo remembers the result of a function and recalculates it only when dependencies change.
Example Without useMemo
function App() {
const [count, setCount] = useState(0);
const heavyResult = slowCalculation(count);
return (
<>
<h2>Result: {heavyResult}</h2>
<button onClick={() => setCount(count + 1)}>Increase</button>
</>
);
}
The function slowCalculation runs on every render, which slows the app.
Fix Using useMemo
const heavyResult = useMemo(() => slowCalculation(count), [count]);
Now the calculation runs only when count changes.
When to Use useMemo
Heavy calculations (sorting, filtering, loops)
Expensive data transformations
Derived values that do not change often
When Not to Use useMemo
3. useCallback — Prevents Function Recreation
In React, functions are recreated on every render. This causes issues when passing functions as props to child components.
useCallback returns a memoized version of a function, so it is not recreated unless dependencies change.
Example Without useCallback
function App() {
const [count, setCount] = useState(0);
const handleClick = () => setCount(count + 1);
return <Child handleClick={handleClick} />;
}
React recreates handleClick on every render → Child re-renders unnecessarily.
Fix Using useCallback
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
Now handleClick only changes when count changes.
When to Use useCallback
When Not to Use useCallback
How memo, useMemo, and useCallback Work Together
These three tools work best when combined.
Example
const filteredList = useMemo(() => {
return list.filter(item => item.active);
}, [list]);
const handleSelect = useCallback((id) => {
console.log(id);
}, []);
const ListComponent = React.memo(function ListComponent({ data, onSelect }) {
return data.map(item => (
<div key={item.id} onClick={() => onSelect(item.id)}>
{item.name}
</div>
));
});
<ListComponent data={filteredList} onSelect={handleSelect} />;
useMemo prevents re-filtering
useCallback prevents regenerating functions
React.memo prevents re-rendering of the list component
This combination results in maximum performance improvement.
Best Practices for Performance Optimization
Use memo on components with stable props
Use useMemo for heavy calculations and expensive operations
Use useCallback when passing functions to child components
Avoid using these hooks everywhere — use them only when a performance issue exists
Profile your app using React DevTools before optimizing
Conclusion
Optimizing React performance becomes easier once you understand how memo, useMemo, and useCallback work. These tools help prevent unnecessary re-renders, avoid expensive recalculations, and stabilize functions that React recreates on each render. By applying these techniques correctly, you can build faster, smoother, and more efficient React applications. With the right balance of optimization and clean code, your app will perform well even as it grows in size and complexity.