loading...
Cover image for Understanding when to use useMemo

Understanding when to use useMemo

rozenmd profile image Max Rozen Originally published at maxrozen.com on ・3 min read

This is an article from MaxRozen.com. You can read the original by clicking here.

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.

What useMemo does

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.

When to use useMemo

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 Array.map()
  • 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.

Why not use useMemo everywhere then?

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.

What about stable references?

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.

Potential mistakes when using useMemo

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.

Discussion

pic
Editor guide