React.memo()
| Since: | React 16.8(2019) |
|---|
React's React.memo is a higher-order component that memoizes a component. Even when the parent component re-renders, child component re-rendering can be skipped if the passed props have not changed.
Syntax
// Basic syntax of React.memo
// 1st argument: the component to memoize
// 2nd argument (optional): a comparison function for props (shallow comparison is used by default)
const MemoizedComponent = React.memo(Component);
// Passing a comparison function
// prevProps: previous props, nextProps: current props
// Return true to skip re-rendering, false to allow re-rendering
const MemoizedComponent = React.memo(Component, (prevProps, nextProps) => {
return prevProps.value === nextProps.value; // true means no re-render
});
Arguments and Return Value
| Argument / Return value | Description |
|---|---|
| Component (1st argument) | The function component to memoize. Cannot be used with class components. |
| Comparison function (2nd argument, optional) | A function that receives the previous and current props. Returning true skips re-rendering; returning false triggers re-rendering. When omitted, shallow comparison is used. |
| Return value | The memoized component, which can be used in JSX just like a regular component. |
Sample Code
An example where React.memo prevents product cards from re-rendering unnecessarily while typing in a search form on a page with a product list and search input.
import { useState, useCallback } from 'react';
// Card component displaying one product
// Wrapped with React.memo so it skips re-rendering
// as long as product and onAddToCart have not changed
const ProductCard = React.memo(function ProductCard({ product, onAddToCart }) {
console.log('ProductCard render:', product.name);
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>{product.price.toLocaleString()} yen</p>
{/* Pressing the button calls the onAddToCart callback */}
<button onClick={() => onAddToCart(product.id)}>
Add to Cart
</button>
</div>
);
});
// Parent component managing the entire product list
function ProductList() {
// State for the search keyword (re-renders on each keystroke)
const [searchText, setSearchText] = useState('');
// List of product IDs in the cart
const [cartIds, setCartIds] = useState([]);
// Product master data (in practice, often fetched from an API)
const products = [
{ id: 1, name: 'Laptop', price: 98000 },
{ id: 2, name: 'Mouse', price: 2500 },
{ id: 3, name: 'Keyboard', price: 7800 },
];
// Fix the callback function reference with useCallback
// This makes React.memo's shallow comparison judge it as "the same function"
// Without useCallback, a new function is generated every time, making React.memo ineffective
const handleAddToCart = useCallback(function(id) {
setCartIds(function(prev) { return prev.concat(id); });
}, []); // Empty dependency array means the function reference is created only once
return (
<div>
{/* Search form. ProductList re-renders on each keystroke */}
<input
type="text"
placeholder="Search products…"
value={searchText}
onChange={function(e) { setSearchText(e.target.value); }}
/>
{/* Display only products matching searchText */}
{products
.filter(function(p) { return p.name.includes(searchText); })
.map(function(p) {
return (
// ProductCard does not re-render if product and onAddToCart have not changed
<ProductCard
key={p.id}
product={p}
onAddToCart={handleAddToCart}
/>
);
})
}
<p>In cart: {cartIds.length} items</p>
</div>
);
}
export default ProductList;
Notes
React.memo is an optimization tool for function components. It controls React's default behavior of re-rendering child components whenever the parent re-renders, and reuses the previous rendering result if props have not changed.
However, for reference-type props such as objects, arrays, and functions, if a new instance is generated each time, they are judged as "different even if the value is the same." Therefore, it is important to stabilize references by combining useCallback for callback functions and useMemo for computed objects.
Passing a comparison function as the second argument allows finer control, such as comparing only specific props. The comparison function returns true to skip re-rendering, and false to allow re-rendering (note that this is the opposite of the return value meaning of shouldComponentUpdate).
Applying React.memo indiscriminately to all components adds the overhead of the comparison itself. It is best to limit its use to components where rendering is costly (high computation or many children).
Related pages: useMemo / useCallback / component / props
Common Mistakes
Passing a callback function as props without useCallback invalidates memo
React.memo checks props with a shallow comparison. If a new function object is generated every time the parent re-renders, the function reference changes, making React.memo ineffective. Memoize the function with useCallback to stabilize the reference.
fighter_ng.jsx
import { memo, useState } from 'react';
const FighterCard = memo(function FighterCard({ name, onSelect }) {
console.log('render:', name);
return <button onClick={onSelect}>{name}</button>;
});
function App() {
const [selected, setSelected] = useState('');
// A new function is generated every time App re-renders
// Even with memo, FighterCard re-renders every time because the reference changes
function handleSelect() { setSelected('Son Goku'); }
return (
<div>
<FighterCard name="Son Goku" onSelect={handleSelect} />
<p>Selected: {selected}</p>
</div>
);
}
After fix:
fighter_ok.jsx
import { memo, useState, useCallback } from 'react';
function App() {
const [selected, setSelected] = useState('');
// Memoize the function with useCallback to stabilize the reference
const handleSelect = useCallback(function() { setSelected('Son Goku'); }, []);
return (
<div>
<FighterCard name="Son Goku" onSelect={handleSelect} />
<p>Selected: {selected}</p>
</div>
);
}
Applying React.memo to all components increases comparison overhead
React.memo adds the cost of the comparison process. Applying it to a component that re-renders frequently with many prop changes can cause the comparison cost to exceed the rendering cost, actually degrading performance. Limit React.memo to components where rendering is costly (high computation or many children).
If you find any errors or copyright issues, please contact us.