How and when to use React.memo
We can use React.memo
to optimize our app’s performance. When React component re-renders, it also re-renders all the tree of children components. That might be excessive because some of the children could stay the same, and there is no need to trigger an additional render for them. React.memo
is our way to say React that it may skip the re-render of the component, if its props didn’t change, and return its cached version instead.
How to use React.memo
Memo serves as a wrapper around our original component. It’s a HOC, so it takes our original component and returns a new, memoized version of it.
const MemoComponent = React.memo(() => {});
If the props of our original component don’t change, React will return the memoized component.
Object.is
comparison is used to determine if props have changed. That means shallow comparison. So, if we want to compare values nested inside an object, we can pass custom comparison function as a second argument.
const MemoComponent = React.memo(
() => {},
(prevProps, nextProps) => {
if (prevProps.value === nextProps.value) {
return true;
}
return false;
}
Read more about the comparison of values
We need to be extra cautious when using the comparison function, since we need to know the exact shape of props
object, as well as ensure we’ve covered all the properties, before coming the conclusion that new props
are the same.
Example
Check the example on code sandbox illustrating the difference between standard and memo components.
import { memo, useState } from "react";
// comparison function
const arePropsEqual = (prevProps, nextProps) => {
if (prevProps.value === nextProps.value) {
return true;
}
return false;
};
const MemoComponent = memo(({ value }) => {
const [internalValue, setInternalValue] = useState(1);
console.log("MemoComponent re-rendered");
const changeInternalValue = () => {
// Memo component re-renders when its own state changes,
// just like a standard component
setInternalValue(internalValue + 1);
};
return (
<div style={{ border: "2px solid red" }}>
<h1>MemoComponent</h1>
<p>MemoComponent props value: {value}</p>
<button onClick={changeInternalValue}>
Change MemoComponent internal state value
</button>
<p>MemoComponent internal state value: {internalValue}</p>
</div> );
}, arePropsEqual);
const StandardComponent = ({ value }) => {
console.log("StandartComponent re-rendered");
return (
<div style={{ border: "2px solid blue" }}>
<h1>StandardComponent</h1>
<p>StandardComponent props value: {value}</p>
</div> );
};
function App() {
const [memoComponentValue, setMemoComponentValue] = useState(1);
const [standardComponentValue, setStandardComponentValue] = useState(1);
const [appValue, setAppValue] = useState(1);
const changeAppValue = () => {
// Parent's state change doesn't trigger
// child memo component re-render,
// because passed props don't change
setAppValue(appValue + 1);
};
const changeMemoComponentValue = () => {
// Change of props trigger Memo component to re-render
setMemoComponentValue(memoComponentValue + 1);
};
const changeStandardComponentValue = () => {
setStandardComponentValue(standardComponentValue + 1);
};
return (
<div>
<button onClick={changeMemoComponentValue}>
Change MemoComponent Value
</button>
<button onClick={changeStandardComponentValue}>
Change StandardComponent Value
</button>
<button onClick={changeAppValue}>Change App Value</button>
<p>App value: {appValue}</p>
<MemoComponent value={memoComponentValue} />
<StandardComponent value={standardComponentValue} />
</div> );
}
When to use React.memo
If the output result of render is always the same with the same props, consider using memo. However, the real benefit comes when component has a lot of internal logic, which is expensive to calculate on every re-render. Otherwise, you might not see the difference.
Note that React.memo
regulates the component’s behavior only when props from the parent component change. If memoized component has its own state or values coming from other sources, it will still re-render as a standard component. This means the usage of useEffect
, useReducer
, and useContext
.
Also, since passed objects and functions are shallowly compared, memo becomes useless if we define those in the parent component on every render because they will always be different. In this case, we would want to use useCallback
or useMemo
in parent component, to memoize our props.
Read more on official docs about whether we need to use
React.memo
all the time, and what best practices we can follow when writing React app, to make most of the memoization unnecessary.
Comments