Boosting React Performance: useCallback
vs. useMemo
Hooks
React's useCallback
and useMemo
hooks are crucial for optimizing performance in your applications. Understanding when and how to use them can save you from unnecessary re-renders and ensure your app runs smoothly. In this article, we'll dive into real-world examples of using useCallback
and useMemo
effectively.
When to Use useCallback
The useCallback
hook returns a memoized version of the callback function, which means it only recreates the function if one of its dependencies changes. This is particularly useful when passing functions as props to child components to prevent them from re-rendering unnecessarily.
Real-time Example: Preventing Unnecessary Re-renders
Suppose you have a parent component that passes a function to a child component. Without useCallback
, the child component would re-render every time the parent component renders, even if the function logic hasn't changed.
import React, { useState, useCallback, memo } from 'react';
const ChildComponent = memo(({ onIncrement }) => {
console.log('Child component re-rendered');
return <button onClick={onIncrement}>Increment</button>;
});
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<h1>Count: {count}</h1>
<ChildComponent onIncrement={handleIncrement} />
</div>
);
};
export default ParentComponent;
Explanation:
Memoizing the child component (memo(ChildComponent)): By wrapping ChildComponent in memo, it will now only re-render when its props change (in this case, when onIncrement changes).
useCallback: This ensures that the handleIncrement function does not change on every render, preventing the child from re-rendering unnecessarily due to prop changes.
When to Use useMemo
The useMemo
hook is used to memoize the result of a function, recomputing the cached result only when one of its dependencies changes. It's useful for optimizing performance in situations where a function performs an expensive calculation.
Real-time Example: Optimizing Expensive Computations
Let's say you have a component that performs a computationally expensive operation, like filtering a large list.
import React, { useState, useMemo } from 'react';
const ExpensiveComponent = ({ items }) => {
const [filter, setFilter] = useState('');
// Using useMemo to optimize expensive filtering
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item => item.includes(filter));
}, [items, filter]);
return (
<div>
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter items"
/>
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
};
export default ExpensiveComponent;
In this example, useMemo
is used to cache the result of filtering the items
array. This way, the expensive filtering operation is only recalculated when items
or filter
changes, avoiding unnecessary computations.
Guidelines for Using useCallback
and useMemo
-
Use
useCallback
when passing functions to child components to avoid unnecessary re-renders. -
Use
useMemo
for expensive computations that don't need to be recalculated on every render. - Avoid overusing them. Memoization adds complexity and can sometimes make code harder to read. Only use them when you identify a performance issue.
- Remember the dependencies array. Always specify dependencies accurately; otherwise, you might run into bugs or unexpected behavior.
Conclusion
React's useCallback
and useMemo
hooks are powerful tools for optimizing component performance by avoiding unnecessary re-renders and expensive calculations. By carefully applying these hooks, you can ensure your React application runs efficiently.
Top comments (13)
Nice article.
One point of feedback. Examples in best practices articles can often be seen as good practices so ensure they are optimal as well.
This code has a hidden bug as the state can be stale if a user is clicking fast
A better way to write this is
Given that the example is trying to showcase the dependency array then maybe rework the example to use another dependency
Great read! Looking forward to more like these!
I will be posting relevant topics to 'Mastering React' and adding appropriate links to each topic.
Thank you, I will try these tips
Very informative, thanks for sharing.
This is not 100% correct, the child component will still rerender since his father state changed.
In order to prevent rerender of the child you will have to memoize the child ( export it with memo(ChildComponent)) so that this will only rerender if the props change.
Then as the article say you need to wrap function inside a usecallback so that when the father component rerender the function that will be passed as props wont change, thus the child component will not rerender.
Thank you for your feedback! 🙏
You're absolutely right. I should have included the step about memoizing the child component using React.memo to fully prevent it from re-rendering when the parent state changes.
This ensures that:
Thanks again for pointing this out! 🙌
This rebuttal is not true.
If the props don't change then react will not rerender the child component even if the parent state changes.
Rerendering child components would be a massive performance hit for the tree if their props don't change
To prevent rerendering of child component you must first ensure it's props are stable meaning they only change when they need to.
If you need to further optimize when the component rerenders then you can use React.memo to exclude some props from consideration or change the default equality check that react uses which is
Object.is
Object.is
is a reference equality checkAdding React.memo on a component without first optimizing it's props will not have any effect
This can be easily tested by adding console logs in a component and changing the state in the parent and seeing if the child component renders a log. Or just use react dev tools.
This is all discussed on the react docs
react.dev/reference/react/memo
The rebuttal you received is mostly correct when they argue that React won't re-render the child component if the props haven't changed. Indeed, React.memo should only be used after ensuring the props passed to the child are stable. However, useCallback plays a role in keeping the function reference stable—a common scenario that could otherwise lead to unwanted re-renders.
Yep that is my point
useMemo and useCallback will be deprecated in React 19, FYI!
Thanks for the heads-up! 🙌
While it's good to stay informed about upcoming changes, a lot of companies are still running on older versions like React 16 or 17. In fact, some organizations take years before upgrading to the latest major versions due to stability concerns or the size of their codebase.
So, even though useMemo and useCallback might be deprecated in React 19, these hooks will likely still be widely used in production environments for quite a while. 😊
But it's always good to be aware of what's coming—thanks again for sharing! 🙏
I don't think they will be deprecated. The react compiler just automatically adds them for you.
That's not really a deprecation