If you are working with React, you have probably heard about the term useMemo.
In a nutshell, useMemo
is a built-in React hook that allows you to memoize output of a function to prevent it from re-running on every component re-render.
So, we could say, the goal of useMemo
hook is to provide a simple way to achieve gains in performance using memoization. But, there is more to it, so let's dive deeper.
How it works
This hook has the following signature.
const memoizedValue = useMemo(compute, dependencies);
You can see that useMemo
accepts 2 arguments.
compute
- function used to compute a result.dependencies
- array of dependencies used bycompute
function.
On the initial render of the component, useMemo
will invoke the compute
function, memoize the result of the compute
function and return the value.
On every component re-render, userMemo
will check if dependencies
are changed. If change is detected, useMemo
will invoke the compute
function again, otherwise it will return previously memoized value.
In programming, memoization is usually implemented in a way that memoization function remembers all inputs and results based on those inputs. Next time, when the same inputs are sent to the memoization function, it will return memoized result.
On the other hand, useMemo only remembers previous inputs and results based on those inputs. As soon as next inputs are not the same as previous inputs, useMemo will run the computation again.
When should it be used
Thing is, useMemo
does not give free performance gains. Running useMemo
will consume CPU time and memory which can hurt performance if used incorrectly.
For this reason, useMemo
should not be used everywhere whenever possible, but rather in some situations where memoization would provide performance benefits.
Overall, make sure that your component does not depend on useMemo
at all. Only if you notice performance issues, introduce useMemo
. Here are some questions you can ask yourself to decide if useMemo
should be used.
- Is the component running expensive calculations?
- Is the component re-rendering often?
- Is the component doing data transformation?
There is no guideline which will tell you when exactly to use this hook. You need to make sure that you understand the problem you are having and then, you will know if useMemo
can help you.
Show me some code
It is true that usage of useMemo
hook may vary and depend on your component, but it is still possible to provide some examples where useMemo
would make sense and where not.
Bad case
As the first example, let's say we want to calculate user age based on the year of birth and the current year. While at it, you might want to add useMemo
there, you know, just in case.
const userAge = useMemo(() => currentYear - yearOfBirth,[currentYear, yearOfBirth]);
Well, there you go, this is a bad usage of the useMemo
hook. Computers are fast and doing simple calculations like this will take basically no time. Just do the following instead.
const userAge = currentYear - yearOfBirth;
When doing calculations on primitive values like numbers, strings, booleans, never wrap them with useMemo. It can take longer to execute useMemo than the calculation itself.
Good case
In this example, there is a function that takes an array of users and formats the activities for every user. Additionally, alongside activities, we want to return users' full name and age.
const activitiesPerUser = useMemo(() => {return users.map((user) => {return {fullName: `${user.firstName} ${user.lastName}`,age: user.age,activities: formatUserActivities(user),};});}, [users]);
This function might take a lot of time if formatting of activities is complicated or if the users
array is large. In this case, it makes sense to wrap the function in the useMemo
hook, especially if the component gets re-rendered multiple times with the same users
object.
Memoize results of functions that are doing expensive calculations and data transformations. Even if component is not being re-rendered at all, it is still good to wrap those functions with useMemo.
Conclusion
Oh, congrats, you made it to the end. I hope that the useMemo
hook is not as complicated anymore.
From my experience, it is always worth memoizing computed non-primitive values, like arrays or objects, that are passed down to the child components. It is even more important if the parent component hosts state variables that are frequently changed. This can prevent unnecessary re-renders of child components.
In general, do not think about performance optimization too much. If your application is not performant, don't worry, you will notice it.