React.memo()
is one of those things that should be a part of every React developer's arsenal. It gives us the ability to memoize a React component. As with any tool, before we delve into how to use React.memo()
, let's understand the problem first.
Why memoize?
Memoization is a general concept which basically means caching the results of some kind of computation for later use. It is an optimization technique which is used quite extensively in the programming world.
An important point to remember is that whenever memoization is used, there must be a criteria which would dicate when the cached results are no longer valid and the computation must be done again.
To understand the problem this solves, consider the following React component:
import { useState, Fragment } from "react";
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
A simple component that keeps a count which can be increased or decreased.
Now let's add another component to <App />
. To keep it simple, we'll create a <Message />
component that returns some kind of message depending on the msgId
that is passed to it as a prop.
function Message(props) {
let msg = "hello, world";
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
We've kept it simple here but imagine this <Message />
component does some heavy computation or perhaps sends a request to an external API in order to get the final message. We'll simulate this situation by adding everyone's favorite console.log()
in the mix.
function Message(props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
Let's update the <App />
component to use <Message />
.
import { useState, Fragment } from "react";
function Message(props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
}
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<Message msgId={1} />
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
In the video below, take special note of the fact that every time count
is changed, the heavy computation is done.
To understand why the heavy computation is done every time
count
changes, check out this post: Re-rendering in React
At this point, take a step back and think about how inefficient our UI is at this moment. count
does not affect <Message />
in any way but still every time count
is updated, the seriously heavy computation is performed. We only want the computation to occur if the msgId
changes because a change in msgId
should result in a different message.
React.memo() to the rescue
React.memo()
is a higher-order component. It accepts a component as its argument and memoizes the result. The memoized result is updated only if the props of the original component are changed.
To use React.memo()
, simply pass your component as an argument and save the result. Our <Message />
component will become:
import { useState, Fragment, memo } from "react";
const Message = memo(function (props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
});
Note: I've only imported
memo()
here. If you haveReact
imported, you can useReact.memo()
instead of justmemo()
.
Now our code looks like this:
import { useState, Fragment, memo } from "react";
const Message = memo(function (props) {
let msg = "hello, world";
console.log("Just performed some seriously heavy computation");
if (props.msgId === 1) {
msg = "hey there!";
} else if (props.msgId === 2) {
msg = "hola!";
}
return <p>{msg}</p>;
});
function App() {
const [count, setCount] = useState(0);
function handleDecrement() {
setCount((oldCount) => --oldCount);
}
function handleIncrement() {
setCount((oldCount) => ++oldCount);
}
return (
<Fragment>
<Message msgId={1} />
<p>Count is {count}</p>
<button onClick={handleDecrement}>-</button>
<button onClick={handleIncrement}>+</button>
</Fragment>
);
}
export default App;
This time, notice that the computation is done when the application is refreshed but the change in count
no longer has that result.
๐๐ป Follow me on twitter: click here
๐๐ป Subscribe to my newsletter