React.memo()
is a higher-order component (HOC) in React that helps optimize the performance of functional components by preventing unnecessary re-renders. It works by memoizing the rendered output of a component based on its props. If the props don’t change, React will skip re-rendering that component and reuse the previous result, which can lead to significant performance improvements, especially in large applications.
In this guide, we will explore how to use React.memo()
effectively, when to use it, and best practices for leveraging it in your React applications to boost performance.
1. What is React.memo()
?
React.memo()
is a function that wraps a functional component and prevents unnecessary re-renders by memoizing the result. By default, it performs a shallow comparison of the component’s props. If the props haven’t changed, React will skip re-rendering the component and reuse the previous rendered output.
const MyComponent = React.memo(function MyComponent(props) {
return <div>{props.title}</div>;
});
- Props Comparison:
React.memo()
performs a shallow comparison of the component’s props. If the props are the same as the previous render, it will skip re-rendering that component. - Memoization: It memoizes the render result based on the component’s props.
2. How Does React.memo()
Work?
When a functional component wrapped with React.memo()
receives new props, React compares the new props with the previous ones using a shallow comparison. If the props are the same, React reuses the previous result. Otherwise, it will re-render the component.
Shallow Comparison: React.memo()
performs a shallow comparison of the props, meaning it checks if primitive values are the same or if references to objects/arrays are the same.
const prevProps = { name: 'John', age: 25 };
const nextProps = { name: 'John', age: 25 };
// Shallow comparison (for primitive values)
prevProps.name === nextProps.name; // true
prevProps.age === nextProps.age; // true
// For objects or arrays, shallow comparison checks if references are the same
const prevArr = [1, 2, 3];
const nextArr = [1, 2, 3];
prevArr === nextArr; // false (they are different references)
- When should you use it?: You should use
React.memo()
when a component’s render output depends on props that rarely change, but the component is getting re-rendered frequently.
3. When to Use React.memo()
React.memo()
is not useful for every component. It is effective when the following conditions are met:
a) Pure Components
If your component’s rendering depends solely on props (i.e., it doesn’t have internal state or side effects), then React.memo()
is very effective. This is because React will only re-render the component when its props change.
Example:
const Button = React.memo(function Button({ label }) {
return <button>{label}</button>;
});
b) Expensive Components
React.memo()
is beneficial for components that are expensive to render. If a component has complex UI or performs heavy computations during rendering, using React.memo()
can reduce unnecessary renders and improve performance.
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
console.log("Rendering ExpensiveComponent");
// Perform complex calculations or rendering
return <div>{data}</div>;
});
c) Stable Props
If a component receives props that don’t change often (e.g., static data, predefined options), React.memo()
can be very useful. It avoids re-renders when the props stay the same across multiple renders.
Example:
const ListItem = React.memo(function ListItem({ item }) {
return <div>{item.name}</div>;
});
If item
doesn’t change frequently, React.memo()
ensures that ListItem
doesn’t re-render unnecessarily.
d) Functional Components
React.memo()
can only be used with functional components. Class components should use PureComponent
or manually implement shouldComponentUpdate()
for similar behavior.
4. Custom Comparison Function in React.memo()
By default, React.memo()
performs a shallow comparison of props. However, you can provide a custom comparison function to control the comparison logic for props.
The custom function receives the previous and next props as arguments and should return a boolean value:
true
if the props are equal (skip re-render).false
if the props are different (trigger re-render).
const MyComponent = React.memo(
function MyComponent(props) {
return <div>{props.title}</div>;
},
(prevProps, nextProps) => {
// Custom comparison logic
return prevProps.title === nextProps.title;
}
);
In this example, the component will only re-render if the title
prop changes.
5. Avoid Overusing React.memo()
While React.memo()
can improve performance, overusing it can actually degrade performance because the comparison itself has a cost. Therefore, it’s important to use React.memo()
selectively for the following scenarios:
- Frequent re-renders with stable props: Use
React.memo()
only for components that render frequently and have props that don’t change often. - Avoid for components that rely on frequent state changes: If your component depends on internal state or constantly changing props, using
React.memo()
will not provide much benefit. - Keep it simple: Avoid using
React.memo()
for very simple or low-cost components, where the benefit of avoiding re-renders is negligible compared to the overhead of the shallow comparison.
6. Best Practices for Using React.memo()
Here are some best practices for using React.memo()
effectively:
a) Use React.memo()
for Pure Functional Components
If your component is a “pure” component (i.e., it renders the same output for the same props), wrap it with React.memo()
to prevent unnecessary re-renders.
const PureComponent = React.memo(function PureComponent({ name }) {
return <div>{name}</div>;
});
b) Optimize Large Lists or Tables
If you’re rendering large lists or tables, React.memo()
can help optimize performance by preventing unnecessary re-renders of individual list items.
const ListItem = React.memo(function ListItem({ item }) {
return <div>{item.name}</div>;
});
You can combine this with useCallback()
to memoize event handlers to prevent passing new function references every time.
c) Combine with useCallback()
for Functions
When passing functions to child components wrapped with React.memo()
, use useCallback()
to avoid unnecessary function recreations and ensure the component does not re-render due to new function references.
const MyComponent = React.memo(function MyComponent({ onClick }) {
console.log("Rendering MyComponent");
return <button onClick={onClick}>Click me</button>;
});
const ParentComponent = () => {
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []);
return <MyComponent onClick={handleClick} />;
};
7. Profiling with React DevTools
To ensure that React.memo()
is providing the desired performance improvements, use React DevTools‘ Profiler to monitor the re-renders of your components.
- Steps:
- Open React DevTools.
- Go to the Profiler tab.
- Record interactions and see which components are re-rendering.
- Analyze if unnecessary re-renders are happening and whether
React.memo()
helps reduce them.