When do you need useMemo (and when don’t you)

It’s confusing when we should use useMemo and when we shouldn’t.

useMemo isn’t free $

useMemo comes with a small cost. So we should understand when it’s a benefit, and when it doesn’t provide any value.

  • Small usage in RAM to memoize & remember the resulting value
  • It costs a small cpu cycle to loop over & compare the dependency array & run internal fn of useMemo

So we should be smart when we use it.

useMemo isn’t always needed

useMemo isn’t needed in certain cases. For example, casting something to Boolean, or doing simple math isn’t an expensive operation and returns a primitive like “boolean” or “number”. These value will always be the same every time they are re-render run 

true === true and 5 === 5.

Conversely, an array or object don’t have equality,

 [] !== [] and {} !== {} and new Date !== new Date.

Two tests of if you need useMemo

  1. Is the calculation to get the value is complex (looping, calculating, initializing a class)?
    • Or is it cheap (comparisons of numbers, boolean, casting types, simple math)
  2. Is the returning complex value (object, array, fn, or class)?
    • Or is it a primitive value that is simple to compare (number, boolean, string, null, undefined)

Examples

  const date = useMemo(() => Date.parse(isoDateString), [isoDateString]);

We should use useMemo

  1. 🛑 Initializes the Date class
  2. 🛑 Returns a Date Object, which is not a primitive

  const isAdmin = useMemo(() => runExpensiveSearch(accounts), [accounts]

We should use useMemo

  1. 🛑 Runs an expensive function to get the value
  2. ✅ Returns a primitive value

In cases where it’s neither an expensive calculation nor a complex object, useMemo isn’t necessary.


-  const isArchived = useMemo(() => Boolean(process?.deletedAt), [process?.deletedAt]);
+  const isArchived = Boolean(process?.deletedAt);

We don’t need useMemo

  1. ✅ Casts to a boolean which is a cheap operation
  2. ✅ Returns a primitive value

-  const numberOfAccounts = useMemo(() => accounts.length, [accounts]);
+  const numberOfAccounts = accounts.length;

We don’t need useMemo

  1. ✅ Getting the length property is cheap
  2. ✅ Returns a primitive value

Just remember the two tests

  • Is it complex / expensive function?
  • Is the value not a primitive?

What about useCallback?!

I’m so glad you asked! The principles are similar. In general, all callback functions should be memoized via useCallback. Functions are considered a non-primitive value thus never have equality unless we memoize them via useCallback.

(Cover Photo: Feldstraße Bunker, Hamburg, Germany – Jonathan Stassen / JStassen Photography)