What is React memo, and how can we use it?
As the headline says, what is memo
in React?
Memo is an optimization technique that we can apply to our components to avoid unnecessary re-rendering.
The function provided by React will accept two arguments, where the second one is optional.
memo(Component, arePropsEqual?)
First argument is our component, and the second argument is a function for checking whether a components props has changed - and if it should re-render. The comparison for props are check by React per default with shallow equal, but if we need to override it, there is a possibility to do so. We will cover that in this guide further down.
Let's make an example
import { memo, useState } from "react"; const MemoizedComponent = memo() => { console.log("Render"); return <div>I don't rerender!</div>; }); const NotMemoizedComponent = ({ currentCount, children }) => ( <div> {currentCount} {children} </div> ); const Parent = () => { const [count, setCount] = useState(1); return ( <> <button onClick={() => setCount(count + 1)}>Add count!</button> <NotMemoizedComponent currentCount={count}> <MemoizedComponent /> </NotMemoizedComponent> </> ); };
In our above example, we now have three components. The Parent
holds the count
state which we change when the button is clicked. The component NotMemoizedComponent
will re-render for each time the button is clicked. And we are also wrapping our memoized component inside the non-memoized component.
In the above example, the <MemoizedComponent />
component, will not re-render, since we are using the memo()
function. If we remove memo()
, we will see in the log that it will re-render everytime we click the button.
Memoized component with object as prop
Let's change our implementation a bit. We will now make the <MemoizedComponent />
accept an object.
const MemoizedComponent = memo(({ myObject }) => { console.log("RENDER"); return <div>{myObject.label}</div>; }); <> <button onClick={() => setCount(count + 1)}>Add count!</button> <NotMemoizedComponent currentCount={count}> <MemoizedComponent myObject={{ label: "I will never change" }} /> </NotMemoizedComponent> </>;
What happens now when we click the button? It re-renders!
So, what is going on? The problem here is that we are passing in an object. And in JavaScript, when we compare two objects, they are not equal.
Example:
const objectA = { firstName: "Peter" }; const objectB = { firstName: "Peter" }; objectA == objectB; // false
Solution
So to solve this, lets make use of the second argument of the memo()
function.
Let's extend our <MemoizedComponent />
a bit
const arePropsEqual = (prevProps, newProps) => prevProps.myObject.label === newProps.myObject.label; const MemoizedComponent = memo(({ myObject }) => { console.log("RENDER"); return <div>{myObject.label}</div>; }, arePropsEqual);
All right, so now we are creating a function arePropsEqual
and passing it in to the memo
function as second argument.
And if we now click the button, everything is working fine and the component will only re-render if the prop has really changed.
Thanks for reading!