Understanding when to use useMemo
It’s pretty common for people to say
Don’t use useCallback/useMemo everywhere!
Without actually explaining cases where you would want to use useCallback/useMemo.
In my last article we learned that useCallback is useful for passing stable references to functions down to the children of a React component, particularly when using those functions in a child’s useEffect.
If you’re scratching your head wondering “…but then, what the hell is useMemo for?”, you’re not alone!
One hint that the React docs give is:
useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).
Which is great if you’re well versed on the significance of passing a function reference versus passing an arrow function that calls the function (while quickly scanning docs for an answer), but for the rest of us, hopefully this article will help.
In short, useMemo calls a function when dependencies change, and memoizes (remembers) the result of the function between renders.
This is in contrast with useCallback which remembers an existing value (typically a function’s definition), between renders.
You want to use useMemo to save yourself from rerunning an expensive calculation to generate a new value, and you want to use useCallback to store an existing value.
This part is where it’s easy to get frustrated. There are a lot of blog posts out there describing useMemo, and then presenting examples of when not to use it.
Unfortunately, it needs repeating: don’t use useMemo until you notice parts of your app are frustratingly slow. “Premature optimisation is the root of all evil”, and throwing useMemo everywhere is premature optimisation.
Here are a couple of cases when you should consider using useMemo:
- You’re noticing a component’s render is frustratingly slow, and you’re passing a calculation to an unknowable number of children, such as when rendering children using
- Your app often becomes unresponsive because you’re fetching a large amount of data, and having to transform it into a usable format
The key is to focus on the problem.
“My app is slow, and calculation-heavy” is a problem that useMemo helps to solve. Run your app through React DevTools Profiler, as well as Google Lighthouse or WebPageTest to understand its performance metrics, wrap your calculation in useMemo, then measure again.
“I just learned useMemo, and want to use it everywhere” is focusing on the solution, and will lead you to premature optimisation, and a potentially slower app.
In short, it’s not a free performance optimisation.
There’s an additional cost (memory usage, for one) incurred when setting up
useMemo, that can very quickly outweigh the performance benefit of remembering every single function’s possible value.
When we use useMemo, we’re taking up more memory in order to free up CPU time. If your app is hammering the CPU with a lot of calculations, that’s when you might consider taking up some memory and use useMemo instead.
If you want to keep a stable reference to an object/array that doesn’t require recalculation, consider using useRef.
On the other hand if you need to recalculate the value when dependencies change, useMemo is the hook for you.
Using useMemo isn’t free of pitfalls as well - one of the big ones is that the cache isn’t guaranteed to keep all of its values between renders. Taken from the docs:
You may rely on useMemo as a performance optimization, not as a semantic guarantee
Translation: the cache isn’t stable!
Meaning if you absolutely want to avoid recalculations with your useMemo call, it’s not guaranteed. For a version of useMemo with a stable cache, see useMemoOne.