# Definition
React.memo
- A higher order component that can be used to get some performance boost if a component renders the same results given the same
props
- Checks for prop changes, and skips rendering the component, and reuse the last rendered result in case previous and current
props
are the same
function PizzaComponent({ name, price }) {
/* render using props */
return (
<div>Pizza: {name}</div>
<div>Total: ${price}</div>
)
}
export default React.memo(PizzaComponent);
This component is a candidate for memoization as given a 🍕 image, it always renders the same 🍕 details.
- By default, it does a shallow comparison of
props
- For custom control on
props
comparison, a custom comparison function can be provided as a second argument.
Let's extend our PizzaComponent
to also show different toppings available for a 🍕. Toppings will be an array of strings. As mentioned above, React.memo
does a shallow comparison of props
, so, in this case, even though the toppings
array remains same, it will still re-render.
So, how do we fix this?
We pass a custom comparison function as follows:
function PizzaComponent({ name, price, toppings }) {
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
return (
prevProps.name === nextProps.name,
prevProps.price === nextProps.price,
prevProps.toppings.every(topping => nextProps.toppings.includes(topping))
)
}
/* render using props */
return (
<div>Pizza: {name}</div>
<div>Total: ${price}</div>
{toppings.map((topping, index) => (
<div key={`${name}_${topping}`}>{topping}</div>
))}
)
}
export default React.memo(PizzaComponent, areEqual);
# Using React memo
Use React.memo
to get a performance boost when:
- Component re-renders often
- Component is usually provided with the same
props
during re-rendering - Component contains a relevant amount of elements to reason
props
equality check
If a component with hooks is wrapped with
React.memo
, then it will still re-render when state or context changes.
# React memo with function as props
Let's add the functionality to order a 🍕 in our PizzaComponent
called onOrder
as follows:
function PizzaComponent({ name, price, toppings, onOrder }) {
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
return (
prevProps.name === nextProps.name,
prevProps.price === nextProps.price,
prevProps.toppings.every(topping => nextProps.toppings.includes(topping))
)
}
/* render using props */
return (
<div>Pizza: {name}</div>
<div>Total: ${price}</div>
{toppings.map((topping, index) => (
<div key={`${name}_${topping}`}>{topping}</div>
))}
<button onClick={onOrder}>Order</button>
)
}
export default React.memo(PizzaComponent, areEqual);
and let's use it as follows:
function App ({ store }) {
const { pizza } = store;
return (
<PizzaComponent
name={pizza.name}
price={pizza.price}
toppings={pizza.toppings}
onOrder={() => placeOrder(pizza.price, pizza.coupon)}
/>
)
}
Since, React.memo
does a shallow comparison, this will do a re-render everytime as it will see the callback function onOrder
as a new prop
everytime.
To fix this, we can wrap the function passed to onOrder
with useCallback
as follows:
function App ({ store }) {
const { pizza } = store;
/**
* This will always return the same function instance as long as pizza is the same.
*/
const onOrder = useCallback(
() => placeOrder(pizza.price, pizza.coupon),
[pizza],
);
return (
<PizzaComponent
name={pizza.name}
price={pizza.price}
toppings={pizza.toppings}
onOrder={onOrder}
/>
)
}
Top comments (0)