`Returns a memoized value.
Pass a “create” function and an array of dependencies. useMemo
will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
Remember that the function passed to useMemo
runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect
, not useMemo
.
If no array is provided, a new value will be computed on every render.`
This is from the documentation of React and of course its accurate but lets break this down to something much simpler to understand.
So lets start with some examples. My first example actually is from a real life application and the second one is something very simple so you can understand the usage of it.
Example 1:
Maybe in our components we have some expensive computations that are being done when our component is being render or when it finishes. So this means everytime we render or re-render , we are going to have the calculations again. So how can we cache this so we don't have to make expensive calculations again and again?
Here we have an array of objects. We have a component that renders some images that when we click on them, we redirect to a different component.
const history = useHistory();
const arrWithvalues = useMemo(
() => [
[
{
img: "./someImage.png",
onClick: () => history.push('/someOtherRoute'),
},
{
img: "./someImage.png",
onClick: () => history.push('/someOtherRoute1'),
},
{
img: "./someImage.png",
onClick: () => history.push('/someOtherRoute2'),
},
{
img: "./someImage.png",
onClick: () => history.push('/someOtherRoute3'),
},
{
img: "./someImage.png",
onClick: () => history.push('/someOtherRoute4'),
},
],
],
[history]
);
So here we have useHistory from react-router library that helps us for changing routes when we are going to click a image with the onClick event.
{arrWithvalues.map((row, idx) => (
<div key={idx} >
{row.map((page) => (
<div key={page.img} >
<img
src={page.img}
alt="Cool Image"
onClick={page.onClick}
/>
</div>
))}
</div>
))}
So when we make the map of the array with the objects we have some data there that is calculated in our component when we trigger some events such as onClick that will trigger the useHistory hook to redirect us to a different component and the other one is the image path. Image if we had even more elements inside in that array... That means even more calculations for our component to render and calculate. So as you see when i declare the arrWithvalues right after it i added useMemo hook that does "cache" the values and compute them according to the array of the dependencies i added, in this care its only the history variable. In this way out component in re-render doesn't take the same time calculating the items / events to render as the first initial time.
But lets take a simpler example:
import React, { useMemo, useState } from "react";
function App() {
const [value, setValue] = useState(0);
const slowFunc = (nr) => {
for (let i = 0; i <= 99999; ++i) {
console.log("expensive calculation");
}
return nr * 2;
};
const doubledNr = useMemo(() => {
return slowFunc(value);
}, [value]);
return (
<div className="App">
<input
type="number"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<div>{doubledNr}</div>
</div>
);
}
export default App;
Here we have a function called slowFunc that makes some very expensive calculations that will effect a lot of our performance on the render / re-render of our component. After that, the function will return us a doubled number that we pass above from our doubledNr function which is our initial state on that value. So as you see i added in front of our doubledNr function the useMemo hook and as dependencies of course our value state.
So the idea here is that we cache the number and we don't have to re-compute the result if our number value doesn't change. This make a big difference in our performance. We only change or call our doubledNr function only we we really need to, in this case we have the value state as dependency.
So should we use this in every component we have?? Of course not. We should use this only in the places we have very expensive calculations to do. If we use this in all cases, even when we don't need it then we will have performance issues because still importing this hook and using it, it still takes memory space and in our component it will take space for "staff" that it will save. But keep in mind that useMemo is not going to do you miracles and save your life because still maybe in same cases you need to make a better function that will not use so much space in memory, so if you do a "horrible" function that takes ages to finish, don't expect useMemo to solve or make the app better so you get a pay rise from your boss :)
But as i said, for the expensive functions that take some time to make calculations, this hook can be a big factor for a better performance in your React app. With a little practice, you will see the benefits so start writing some code and tests those things because maybe just by reading this blog or other blogs its not the best solution for you to understand things, unless you try yourself!