Understanding React.memo: How to Avoid Unnecessary Re-renders
A practical guide to understanding how React.memo works, when to use it, and when it's better to avoid it.
TL;DR
React.memo
is a useful tool to prevent unnecessary re-renders of functional components by memoizing them based on their props. In this post, we explore how React.memo
works through hands-on examples, explain the limitations of shallow prop comparisons (especially with arrays and objects), and show how to use custom comparison functions for better control. We also discuss when it's better to refactor your component tree instead of reaching for memoization. Use React.memo
wisely—it can boost performance, but overusing it can do more harm than good.
Understanding React.memo
: When and Why to Use It
React.memo
is a performance optimization tool provided by the React team. It helps you avoid unnecessary re-renders by memoizing your components—only re-rendering them when their props change.
Let’s look at a simple example to see how it works.
The Setup
Source code: https://github.com/mycodecanvas/memo-demo/tree/main
Imagine we have a small React app with the following features:
A button that toggles a “Hello World” message.
A
Box
component that receives atitle
anddescription
from the parentApp
component.
Note: In the example below, each console.log
message appears twice because React is running in Strict Mode, which intentionally double-invokes certain functions in development to help detect potential issues.
To understand how rendering works, I’ve added console.log
statements in both the App
and Box
components.
When you run the app:
Both the
App
andBox
components render initially.When you click the Click Me button, the
App
component re-renders due to a state update (isOpen
toggles).Since the
Box
component is a child ofApp
, it also re-renders—even though its props haven’t changed.
This behavior is expected in React. Whenever a parent re-renders, its children re-render too, unless React knows they haven’t changed. But this raises a good question:
Should a child component like
Box
re-render if none of its props changed?
Enter React.memo
Source code: https://github.com/mycodecanvas/memo-demo/tree/memo
This is where React.memo
comes in. It's a higher-order component that wraps around your functional component and tells React: “Only re-render this component if its props have changed.”
Don’t confuse this with useMemo
, which is a React hook used to memoize values, not components. (If you're curious, I have another post explaining useMemo
in detail.)
To use React.memo
, simply wrap your component when exporting:
Now, when you click the button in App
, you'll see that App
re-renders, but Box
doesn't—as long as its props haven’t changed. That’s exactly what we want.
Should We Memo Everything?
Not necessarily. React.memo
isn't free—it performs a shallow comparison of props each time the parent renders. For most simple components, this check is fast. But overusing it, especially on deeply nested or complex props, can hurt performance instead of improving it.
An Alternative: Smarter Component Design
In some cases, you can avoid unnecessary re-renders without using React.memo
at all—just by rethinking your component structure.
For example, if you move the button and isOpen
state into a separate component, clicking the button won’t cause the App
(and therefore the Box
) to re-render. This change alone avoids the extra rendering without needing any memoization.
A More Complex Example: Objects and Arrays
Source code: https://github.com/mycodecanvas/sort-demo/tree/main
Here's where things get tricky.
Imagine we have a Table
component that receives a data
prop—an array of arrays. The parent App
component has two buttons: Sort by UserID and Sort by Title. By default, data is sorted by userID
.
Clicking a sort button updates the sorted data and triggers a re-render. That's expected.
But what happens if you click the same sort button twice, or click Sort by UserID immediately after the app loads—when the data is already sorted? Technically, nothing has changed. However, the Table
component still re-renders.
As mentioned earlier, when the App
component re-renders, its child components—including Table
—also re-render. You might expect that wrapping Table
with React.memo
would prevent this, but it doesn’t. Why not?
Because each time you call setState
with a new array (even if its content is the same), you're creating a new reference, and React.memo
performs only a shallow check. That means it only checks if the reference is different—not whether the actual data inside the array changed.
Custom Equality Function
Source code: https://github.com/mycodecanvas/sort-demo/tree/memo-custom-arePropsEqual-function
To fix this, React.memo
accepts a second argument: a custom props comparison function.
This arePropsEqual function tells React, “Only re-render if the actual data content has changed.” Below is an example of arePropsEqual function that I defined for the Table component:
Now, if you click the same sort button again, React skips re-rendering the Table
—because your equality function correctly detects that the data hasn't changed.
But if you click a different sort button and the data order does change, the component re-renders as expected.