Frontend Development 9 min read

Mastering React’s useMemo: When to Cache, Shallow vs Deep Comparisons

This article explains how React's useMemo hook caches expensive calculations, covers its default shallow reference comparison, and shows techniques such as JSON.stringify and lodash.isEqual for deep comparison to prevent unnecessary re‑renders.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
Mastering React’s useMemo: When to Cache, Shallow vs Deep Comparisons

Basic Usage

useMemo is a React Hook function used to cache calculation results to avoid unnecessary recomputation. It takes a function and a dependency array and returns a cached value. When any value in the dependency array changes, useMemo recomputes and returns a new cached value.

The basic syntax for useMemo is:

<code>import React, { useMemo } from 'react';

const MyComponent = () => {
  const memoizedValue = useMemo(() => {
    // perform some calculation or logic
    // return the result
  }, [dependency1, dependency2]);

  return (
    <div>
      {/* use the cached value */}
      <p>{memoizedValue}</p>
    </div>
  );
};
</code>

In the example above, the useMemo hook caches a calculation result or logic. The function passed as the first argument is called once on initial render and only recomputed when dependencies change. The returned value is cached and reused in subsequent renders, avoiding expensive recomputation and improving performance.

React’s useMemo can help reduce component re‑renders by caching results when expensive calculations are involved or when prop changes do not affect the rendered output. The following example shows how to use useMemo to reduce re‑renders:

<code>import React, { useMemo } from 'react';

const MyComponent = ({ data }) => {
  // useMemo to cache calculation result
  const memoizedValue = useMemo(() => {
    // expensive calculation
    // return result
  }, [data]);

  return (
    <div>
      {/* use the cached value */}
      <p>{memoizedValue}</p>
    </div>
  );
};
</code>

In this example,

data

is a prop passed to MyComponent. By adding

data

to the dependency array, React only recomputes when

data

changes; otherwise it reuses the previous result, avoiding unnecessary re‑renders.

Shallow Comparison

useMemo uses reference equality (shallow comparison) by default. For objects, the reference may change even if the values are the same:

<code>const obj1 = { name: 'John', age: 30 };
const obj2 = { name: 'John', age: 30 };

obj1 === obj2 // false
</code>

When you want to avoid recomputation despite a new reference, list each property in the dependency array:

<code>const obj = { name: 'John', age: 30 };
const memoizedValue = useMemo(() => {
  // expensive calculation
  // return result
}, [obj.name, obj.age]);
</code>

For complex objects or arrays, enumerating every property becomes cumbersome. Example with an array:

<code>console.log(fruits); // => ['apple', 'banana', 'grapes']

const yellowFruits = useMemo(
  () => fruits.filter(fruit => fruit === "banana"),
  [fruits]
);
// => ['banana']
</code>

When

fruits

changes, the reference of

yellowFruits

changes, but its contents may stay the same:

<code>console.log(fruits); // => ['apple', 'banana', 'grapes', 'pineapple'] // new content, new reference

const yellowFruits = useMemo(
  () => fruits.filter(fruit => fruit === "banana"),
  [fruits]
);
// => ['banana'] // same content, new reference
</code>

Deep Comparison

When shallow comparison is insufficient, you can deep‑compare dependencies. One approach is to stringify the dependency array:

<code>import React, { useMemo } from 'react';

const MyComponent = ({ data }) => {
  const stringifiedData = JSON.stringify(data);

  const memoizedValue = useMemo(() => {
    // expensive calculation or logic
    // return result
  }, [stringifiedData]);

  return (
    <div>
      {/* use the cached value */}
      <p>{memoizedValue}</p>
    </div>
  );
};
</code>

Be careful: if the stringified version changes even when the actual data hasn't, it can trigger unnecessary recomputation.

Another solution is to use a custom equality check such as

lodash.isEqual

:

<code>const _ = require('lodash');

const obj1 = { name: 'John', age: 30 };
const obj2 = { name: 'John', age: 30 };
const obj3 = { name: 'John', age: 25 };

console.log(_.isEqual(obj1, obj2)); // true
console.log(_.isEqual(obj1, obj3)); // false
</code>

In a functional component you need

useRef

to keep the previous value:

<code>import React, { useMemo, useRef } from 'react';
import _ from 'lodash';

const MyComponent = ({ data }) => {
  const ref = React.useRef(data);

  const memoizedValue = useMemo(() => {
    if (!_.isEqual(ref.current, data)) {
      // expensive calculation or logic
      ref.current = data;
    }
    // return result
    return ref.current;
  }, [data]);

  return (
    <div>
      {/* use the cached value */}
      <p>{memoizedValue}</p>
    </div>
  );
};
</code>

This pattern updates the cached result only when

data

actually changes, improving performance.

Some developers propose adding a third argument to useMemo for a custom comparator, but React does not currently support this API.

Summary

useMemo is a useful tool for optimizing React component performance by avoiding unnecessary calculations. It performs shallow comparison of dependencies; for deep comparison you can use JSON.stringify or lodash.isEqual. Similar concepts apply to useEffect and useCallback.

frontendperformanceReactuseMemodeep-comparisonshallow-comparison
KooFE Frontend Team
Written by

KooFE Frontend Team

Follow the latest frontend updates

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.