How (and when) to use useMemo() and useCallback()

Created: Jan 24th 2023 - Updated : Jan 25th 2023

Many React developers struggle to understand the difference between useMemo() and useCallback() and when to use each of these hooks. In this blog post I'll try to explain this and make it a bit clearer for you.


Please note : It's best if you read this blog post after you have some knowledge of React. If you're an absolute React beginner, I'd suggest you bookmark this blog post and come read it in a couple of weeks, when you have a better understanding of React


re-rendering ?

React "re-renders" to keep our UI in sync with our application state as it gets updated. This is one of React's main features. Each re-render is a "snapshop" of the UI in a specific moment, based on the application's state. We can think of it as PostIt notes, stacked one on of another, that hold the state of the application. Everytime we add a new PostIt note, the UI gets "re-rendered" to reflect it.

What gets re-rendered, is a "picture" of what the DOM should look like. It's presented as HTML but in reality it's a bunch of JS Objects. On every re-render, React compares the new snapshot, with the last one and figures out which UI Components it needs to update.

While React is greatly optimized to manage these updates, it might take a while to create these snapshots under certain circumstances, making the UI updates feel "slow" or "laggy", leading to performance issues.

And this is where useMemo() and useCallback() hooks come into play, by reducing the amount of work needed to be done in a render and reducing the number of times a component needs to re-render.

useMemo() hook

useMemo() allows us to "memorize" a value between renders. This can be helpful when we have heavy computations to make or when we need to preserve our references between renders

Case 1 : Heavy computations

Let's suppose we create a tool that returns the fibonacci suite up to a given number. As you can imagine, in case of a huge number input, there will be a great amount of calculations to be executed.

For every user picked number, we have to go (possibly) through tens of thousands of numbers which is computationally intensive no matter how much we optimize our algorithm.

As you can imagine, we need to perform the calculations sometimes (when user changes the input value) but not everytime the component is rendered.

For example, let's add a digital clock in our previous example :

In this example, we have added a new piece of state, called 'time', which gets updated every second to reflect the current time.

But what also happens is that the fibonacci calculation is re-run on every render, which means we are constantly regenerating the list of fibonacci suite number even when the user didn't update the input number. You can see that we console.log 'Rendered' every second.

This can create performance issues because JavaScript is a single threaded process. Keeping this thread extremely busy and can make our application feel sluggish, especially on lower-end devices.

This is when useMemo comes into play. It allows us to "skip" these re-calculations since it reuses the already generated result unless the input is altered by the user.

useMemo.js
const fibonacciSuite = React.useMemo(()=>{
	return getFibonacciSuiteNumbers(selectedNum)
   }, [selectedNum])

useMemo takes two arguments:

  1. A block of work to be executed wrapped up as a functions.
  2. A list of dependencies.

How it works : React checks if any of the values in the dependency list has changed. If any of the values has changed, React will re-run the supplied task and calculate a new value. Otherwise, it will just keep the previously calculated value. In other words, it's as if we were saying : "Recalculate the fibonacci sequence ONLY when the selectedNum value changes." This is known as memoization, and this is why the hook is called "useMemo"

Here's an example of how we could do this :

Case 2 : Preserving references

In the following example we have a "Cards" component which displays a set of cards. The information about the cards is saved in an Array called "cards". We also have some unrelated state such as "time".

As you can see we are wrapping it in a useMemo hook. "Boxes" is a pure component, meaning that it should only re-render whenever its props change (in this case "cards") :

-so it shouldn't recalculate its values when "time" state gets updated, right ? -WRONG -But useMemo should protect the component from this issue.. what is happening ?

This goes back to the basics of JavaScript and how it works under the hood. In JavaScript, everytime React re-renders, we create a new array. And in JavaScript, two arrays can be equivalent in value, but they can be referencing different objects

As you can see in the example... both arrays are exactly the same in their contents, but they are not equal in terms of reference.

so when we use '===' we don't only check the contents, but we also check if the two values are actually the SAME THING.

Imagine having two identical twins... they look the identical but they are 2 completely different persons.

Since components in React are mainly JavaScript Functions... everytime we render it, we are invoking that function, creating a new array in the process.

When the "time" state changes, our "App" component re-renders, a new "cards" array is constructed, and by passing it onto our "Cards" component, we get a re-render since the previous Array is not the same as our new Array in terms of reference

We can solve that with the following piece of code :

reference.js
const cards = React.useMemo(()=> {
	return [
    	{ cardId: 0, cardContent: 'I am a card' },
        { cardId: 1, cardContent: cardText },
      ]
}, [cardText]

We do want the cards to re-render when "cardText" changes, but not when "time" does. useMemo() preserves the reference to "cards" between re-renders, that way it will ignore all the renders that don't affect the UI.


Check the console log to see the rerenders in real time

useCallback() hook

We have "useMemo()" covered... but what about "useCallback()" ?

I have good news for you ! "useCallback()" is the same exact thing, but for functions instead of arrays/objects. Functions are compared by reference as well, this means that if we define a function within our component, it will be replaced on every single render, creating a unique function each time.

This example containts a typical todo app, with the ability to increment a counter

The "Todos" component is memoized thanks to React.memo but as we already saw before, it'll be rerendered whenever the counter value changes.

We could solve this with "useMemo()" as we've already learned , while this works, there is a better way.

useCallback() is exactly the same as useMemo(), but it's built to memoize functions. Put differently the following two examples have the same result

useMemo_VS_useCallback.js
React.useCallback(function helloWorld(){},[])
// and
React.useMemo(() => function helloWorld(){},[])
// will function the same way

Seing this, you can easily deduct that useCallback is syntactic sugar. It exists to make our lives easier when we want to memoize functions.


Ok, but how often should I use them ?

I can already hear some of you asking the following question : "Vasili, thank you for explaining this to me, but when and how often should we REALLY use them ?

In my opinion, the best moment to use these hooks is when you really have issues with performance. When you notice your app is getting sluggy and slow, you can use the React profiler and narrow the issue to the components that have slow renders. Most of the cases your remedy will be to restructure your application, but in some cases useMemo and useCallback can help you speed things up.

here are some scenarios that I use these hooks without any second thought :

Context providers

When sharing data with context within an application, it's common to pass big objects as the value attribute.

AuthContext.js
const AuthContext = React.createContext({});
function AuthProvider({ user, status, children }){
  const memoIzedAuthState = React.useMemo(() => {
    return {
      user,
      status,
      
    };
  }, [user, status]);
  return (
    <AuthContext.Provider value={memoizedAuthState}>
      {children}
    </AuthContext.Provider>
  );
}

Since this context might be consumed by n number of pure components, it is beneficial to use useMemo. Otherwise, all the components consuming the context will have to re-render if AuthProvider's parent happens to re-render.


Finally you reached the end of this blog post. Please remember to share and to bookmarki this post if you enjoyed reading it and if it helped you. Thanks in advance, Vassili