Skip to content
Today's Tech Byte: Memoization with useMemo, useCallback and React.memo

Today's Tech Byte: Memoization with useMemo, useCallback and React.memo

Posted on:October 31, 2023

I’m sure most of you have used React.memo, useMemo or useCallback before, but have you ever wondered how it works?

First, let’s start by explaining why we need memoization in the first place.

Why do we need memoization?

Comparing two objects in JavaScript is not as easy as comparing two numbers or two strings. For example, if we have two objects a and b with the same properties and values, a === b will return false. This is because a and b are two different objects in memory.

const a = { id: 1 };
const b = { id: 1 };
a === b; // will always be false

To compare two objects, we need to make sure that the reference in b is exactly the same as the reference in a.

This exact behavior causes issues sometimes in React. Let’s take a look at the following example:

function ChatRoom() {
  function createConnection() {
    // ...
  }

  useEffect(() => {
    createConnection();
  }, [createConnection]);
  // ...
}

Each time the ChatRoom component renders, a new createConnection is created. This means that the useEffect hook will run every time the component renders, even if the createConnection function is the same.

And this is where memoization comes in handy. It allows us to cache a value between renders.

useMemo & useCallback

In the previous example, we want to cache the createConnection function between renders. To do so, we can use the useCallback hook.

function ChatRoom() {
  const createConnection = useCallback(() => {
    // no dependencies, reference won't change between re-renders
  }, []);

  useEffect(() => {
    createConnection();
  }, [createConnection]);
  // ...
}

useCallback will return a memoized version of the createConnection function. This means that the createConnection function will only be created once, and the same function will be used in the useEffect hook.

If we needed to cache a value that is not a function, we would use the useMemo hook instead.

function ChatRoom() {
  const connection = useMemo(() => {
    return createConnection();
  }, []);

  useEffect(() => {
    connection();
  }, [connection]);
  // ...
}

Even though seems like a quick and easy solution, it’s not always the best one. In fact, memoization can cause more harm than good if not used properly.

In the case of useMemo, we need to make sure that our computation is expensive enough to justify the overhead of memoization, also if our component never re-renders, memoization is useless, and we’re just wasting memory.

React.memo

React.memo is a higher-order component that will return a memoized version of a component. It will only re-render the component if its props have changed.

import { memo } from "react";

const SomeComponent = memo(function SomeComponent(props) {
  // ...
});

Again, in the case of non-primitive props, React.memo will compare the references of the props. This means that if we pass a new object as a prop, the component will re-render even if the properties and values are the same.

To avoid this, we can use the useMemo and useCallback hooks to memoize the props.

import { memo, useMemo } from "react";

const SomeComponent = memo(function SomeComponent(props) {
  // ...
});

const App = () => {
  const props = useMemo(() => {
    return {
      id: 1,
      name: "Siham",
    };
  }, []);

  return <SomeComponent props={props} />;
};

The problem with this approach is how fragile it is. If we spread the props in the SomeComponent component, the component will re-render even if the props haven’t changed.

import { memo, useMemo } from "react";

const SomeComponent = memo(function SomeComponent(props) {
  // ...
});

const App = () => {
  const props = useMemo(() => {
    return {
      id: 1,
      name: "Siham",
    };
  }, []);

  return <SomeComponent {...props} />;
};

There are many other cases where memoization can be broken, There is no easy way to solve this problem. The best solution is to avoid passing non-primitive props to the SomeComponent component.

Conclusion

Memoization is a powerful technique that can help us improve the performance of our React applications. However, it is also easy to misuse. Therefore, we need to be careful when using it, and opt for a different solution if possible.

If you have any suggestions or questions, feel free to reach out to me on Twitter or LinkedIn.

P.S. this post is inspired by the book, but it’s not a summary of it. I’m just sharing what I learned from it. If you want to learn more about React, I highly recommend you to read it.