What is the difference between useMemo and useCallback?

November 23, 2020

A post by Will Mayger
Twitter . Instagram

Your complete guide to useMemo vs useCallback, and everything in between.

The react hooks, useMemo and useCallback, are probably one of the biggest causes of confusion when you come across them compared to any other hook.

In this article we will go over what they both are, the differences, when you should use them, and when you should not use them.

I will also be making sure to break down useMemo and useCallback in order to explain them in easy language so that everyone can learn and understand them without the complexity and too much technical jargon!

What do they do?

So to start with I think it will be a good idea to get to know what useMemo and useCallback do, and why people use them before we get into the differences and when you should use them or not.

If you haven’t looked already, you can find the docs for useMemo and useCallback on these links, this article will cover everything but it is always useful to have the official documentation for reference!

Both useMemo and useCallback are react hooks which means they are for use in functional react components.

You probably know and use other, more common, hooks like useState and useEffect.

A core difference between useMemo and useCallback when compared to other react hooks is something called memoization.

We will get into detail on memoization in the next section, so don’t worry if you don’t understand it right now.

Simply put, these two hooks are primarily based around performance between renders.

Here are the basic definitions from the official react hooks api reference:

useMemo: Returns a memoized value.

useCallback: Returns a memoized callback.

Memoization

So in the docs for useMemo and useCallback, it talks a lot about something called memoization.

Memoization is the central part of what the useMemo and useCallback hooks do, so it is worth learning, but don’t worry we are going to break it down and make it easy to understand!

At the face value memoization sounds pretty intimidating and scary, luckily it isn’t actually so bad.

So, what is memoization?

Memoization can pretty much be summed up as a kind of cache for the value returned from a function.

All that means is that when a function has been run, instead of having to run the function again to get the results, it remembers what the results were from the first time you ran it.

In other words it remembers what the result is.

Then, when you need to run the function again with the same arguments, it will give you back the result that it memoized/remembered from the first time.

You can imagine this as basically the same as storing the results from a function in a variable, and then using the variable rather than calling the function each time.

Even though memoization is a computing term, we do this all the time in our everyday life as well.

If someone asks you, what is 5 + 5, do you have to work it out or do you already know the answer is 10?

It is the same thing, 5 + 5 is our function, 10 is the result and instead of calculating it every time, we remember the result so we don’t have to calculate it again which leads to a faster response.

So how does useMemo and useCallback fit in with it and why don’t we just store it as a variable instead?

Both the useMemo and useCallback react hooks use memoization, it is actually where the name useMemo comes from.

The reason we want to use these methods is because in react, components render and rerender.

So if you have created a function or a variable in your component, the second it gets rerendered all the values will be lost unless you persist it into the next render, which is exactly what react state does.

useMemo and useCallback both use this so that the results and functions you pass them do not constantly get called again and again.

So why should you use these hooks when you can just store all the results outside the scope of the component?

Well, there is no real need to try and create your own version of this because the hooks have already been created and are simple and easy to use.

You would effectively be re-creating the wheel which is usually never a good thing in coding.

With that being said, for most of the time when you can define and use your function outside of your react component, it will probably be better than using these hooks.

Why aren’t these hooks used more often then?

To answer why they aren’t used more let’s first go back to my 5 + 5 = 10 example.

In a React application you should not try to use useMemo or useCallback for this function/result. That is because it is a very simple function that is not computationally expensive.

When you factor in how much extra code is needed behind the scenes of those two hooks it would actually be slower than just calculating the result each time, we will go into this in a bit more depth later on when we go over when you should and should not use useMemo and useCallback.

One other thing you need to consider is the lifespan of your application, most react applications are designed to be run every time you refresh your browser so whether that is 1 minute or a few hours, they aren’t designed to be run for a very long time.

This means there isn’t much gain to remembering a simple value over that short amount of time.

So, what is the difference between useMemo and useCallback?

What is the difference between useMemo and useCallback?

When using useMemo and useCallback, both hooks accept a function and an array of dependencies. In as few words as possible, the difference between useMemo and useCallback is that useMemo will memoize/remember the value that is returned whereas useCallback will memoize/remember the function.

If you have a computationally expensive bit of code that accepts arguments, and returns a value then you would need to use useMemo so you can keep referencing that value without re-running the computationally expensive code again between renders until the arguments change.

If you need a function to persist between renders then you would need useCallback so it remembers that instance of the function. You can think of this a bit like creating a function outside the scope of your react component, so the function persists even when your code is rerendered.

When I say computationally expensive, all it really means is anything that is going to be resource heavy and take a lot of time or space to run.

For example a nested loop (O(n²) in Big O notation) over a moderate to large array might be something that you would consider to be “computationally expensive”.

Before we carry on quiz yourself with this code example:

Look at this useMemo example:

const myNumber = 5
const answer = useMemo(() => myNumber + 5, [myNumber])

Here, what has useMemo remembered/memoized? Has it remembered () => myNumber + 5 or has it remembered the result, which would be 10?

If your answer was the result, 10, then you were correct!

useMemo will remember the result of the function that you provide it with until the dependencies (myNumber) have changed.

Look at this useCallback example:

const myNumber = 5
const answer = useCallback(() => myNumber + 5, [myNumber])

Here, what has useCallback remembered/memoized? Has it remembered () => myNumber + 5 or has it remembered the result, which would be 10?

This time, if your answer was the function, () => myNumber + 5, you would be correct!

useCallback will remember the function that you provide it with until the dependencies (myNumber) have changed.

How does the dependency array work with these hooks?

You may be familiar with the dependency array of useMemo and useCallback if you have been using the react hook useEffect.

Now, if you were to compare the dependency array for useMemo vs useCallback vs useEffect, it would be identical to the users(us) of the hooks.

The idea is that you provide the array with values/variables that you want to use inside the function that you also provide to the hook.

Here is a code example of useMemo vs useCallback vs useEffect:

export default function MyReactComponent({ myNumber }) {
  useEffect(() => {
    // I will be run every time myNumber changes
    console.log(myNumber)
  }, [myNumber])

  const calculation = useMemo(() => {
    // I will also be run every time myNumber changes
    return myNumber * 2
  }, [myNumber])

  const calculate = useCallback(() => {
    // I will also be run every time myNumber changes
    return myNumber * 2
  }, [myNumber])

  return <div />
}

If “myNumber” changes then the functions will be run again with the new value.

This ensures that you will always have up to date values in your code when you use these hooks so you don’t need to worry about stale (old values) variables whenever the hook gets run again.

Another quick quiz:

If the variable myNumber suddenly changed to 6, rather than 5, what would happen?

Which hooks would be re-run?

All of the hooks would be re-run in this instance because they all have the variable myNumber as a dependency in their dependency arrays!

Would the values of the hooks be updated to reflect the new myNumber value?

Yes, all the values would have been updated to reflect the updated value of myNumber!

Last quiz question: Would the following useMemo hook update the variable formattedString if the value of myString changes?

export default function WillItUpdate({ myString }) {
  const formattedString = useMemo(() => {
    return `Here is my string: ${myString}!`
  }, [])

  return <p>{formattedString}</p>
}

Answer: No, it will not update! In the above question/example, the variable formattedString won’t update because we have not included the myString variable in the dependency array, be very careful of this!

For the most part you should always include all your variables within the dependency array to avoid stale(old) variables/dependencies!

When to use useMemo and useCallback

Let’s look at a few examples of react components that use these functions to get a better understanding of how and why they are used.

Example 1 - with useCallback vs useEffect:

import React, { useEffect, useCallback } from "react"
import { sendMessageToUser, updateUserName } from "./api"

// in this example the prop newMessage is a function that returns another function (closure).
export default function UseCallbackVsUseEffectExample({
  userMessage,
  userName,
  newMessage,
}) {
  const createMessage = useCallback(
    newMessage(`Hi there, here is your message: ${userMessage}.`),
    [userMessage]
  )

  useEffect(() => {
    updateUserName(userName)

    const newMessage = createMessage()
    sendMessageToUser(newMessage)
  }, [createMessage, userName])

  return <p>Your username was updated and your message has been re-sent</p>
}

Let’s start by looking at the problem that is being solved here.

Without the use of useCallback, the function would be a new function for every render, this would then cause the useEffect hook to be run on every render because we would always be passing a new function into it’s dependency array each time. That would result in a bug that would send a message and update the username on every render.

That is really not ideal, and it is where useCallback comes to the rescue!

useCallback is providing us with something called referential equality between renders.

Referential equality, is simply where some value is comparably the same whenever you reference it.

Here is a visual exmaple of referential equality:

// Does not have referential equality
() => {} === () => {} // false
// Does have referential equality
const a = () => {}
a === a // true

useCallback, in this example, is saving/remembering that exact function and persisting it for every render. What that means is that the useEffect hook, will now be receiving the same function in its dependency array so it will not be run on every render.

The only times this code will now be run is when the userName or the userMessage changes, which is what we want.

Example 2 - useMemo with expensive function:

import React, { useEffect, useMemo } from "react"
import { massiveArray } from "./massiveArray"

export const filterAndMapMassiveArrayToNumber = number => {
  // There are much better ways and algorithms to do this sorting,
  // this is only for illustrating the purpose of useMemo.
  return massiveArray
    .map(item => ({
      ...item,
      value: item.value * myNumber,
    }))
    .filter(item => item.value > 10)
}

export default function UseMemoWithExpensiveFunctionExample({ myNumber = 1 }) {
  const finishedArray = useMemo(
    () => filterAndMapMassiveArrayToNumber(myNumber),
    [myNumber]
  )

  return (
    <ul>
      {finishedArray.map(item => (
        <li key={item.id}>{item.value}</li>
      ))}
    </ul>
  )
}

As you can see in this example we have a massive array, let’s say it has thousands upon thousands of items. In the example, each time we run the filterAndMapMassiveArrayToNumber function, we are looping over the array twice, spreading an object, creating a new object, and multiplying a number amongst other things.

It goes without saying that the function is not well optimized and can be done in much better ways, but it is perfect for illustrating what useMemo does and why it is useful.

It would be fair to say that when the function is run, it will take some time to finish, and the last thing we would want is to do would be to run that on every render.

So instead, what we can do is to make sure it only ever runs when the myNumber property changes.

When the number changes the function will run and useMemo will remember the result so we do not have to run that function on each render which will end up saving us a lot of time and resources.

When not to use useMemo and useCallback

You now have a couple of examples of when it might be a good idea to use these hooks, but now we need to cover the more important topic of when not to use them.

It is quite a common thought that using useMemo or useCallback will give you a performance boost, and understandably so, you are saving on extra function calls. Unfortunately though, that is not always the case.

Using these hooks does come at a cost, and that is the underlying code for each hook.

Even though your function may get called less often, useMemo, or useCallback will then be called on every render, afterall you would be adding an additional function to your react component.

Inside both of those functions there is code that needs to run in order to determine whether or not to run your function to get a new value.

The code involved in memoizing your function or value, as well as the code needed to check if it needs to be updated or not can often be much larger and much more expensive than the code in your own function that you pass into the hooks.

This is something you will have to decide yourself each time you are faced with this choice.

As a general rule of thumb, it might be a better idea to avoid using useMemo and useCallback unless you have a function that you can see is going to be really resource heavy and you can’t find another way to optimize/reduce how often it will be called.

For more detail on when you should not use useMemo and useCallback I highly recommend giving this post a read it goes over the performance aspect of useMemo and useCallback in detail.

With that all said there are a few things you can do before using useMemo and useCallback that will provide you with a good performance boost with less cost that you should always consider first.

Here is a checklist that you can run through each time you are unsure whether you should use useMemo or useCallback:

  1. Can you move and/or run your function outside of your react component? One of the first things you should try is removing your function definition from your react component, and if possible the execution of it as well.
  2. Instead of using useCallback, can you move your function inside the useEffect hook so it no longer becomes a dependency? This is a great alternative to using useCallback, it saves you having to worry about the extra hook and any confusing dependencies like functions.
  3. Can you improve your functions algorithm to be more efficient to the point it is not so resource heavy? In the examples above, we outlined that some of those functions were not properly optimized and it might be the same for your function, try thinking of a better, more efficient algorithm for your code.
  4. Can you restructure your react components to avoid the problem entirely? It is quite common when we get so far into a project that it becomes a pain to refactor and deal with a problem at the root cause, so sometimes we might take the easy route and just add in a workaround because it seems quicker. In reality that workaround will end up costing you a lot more time and effort in the future so you may as well try and refactor the code now.
  5. How often will this component be re-rendered? If not very often, is it worth using useMemo or useCallback? If you have a component that is only rendered once or twice in the lifetime of your application, then it may very well be possible that you don’t need to add in one of these hooks to deal with it because it may actually negatively affect your performance.
  6. Are you using useMemo or useCallback because your component is being rendered more often than it should be? Again, in this case it might be better to find the root cause of the issue and fix it rather than using these hooks as a workaround.
  7. If your component is being rendered many times but you cannot do anything about it, it might be better to consider using React.memo instead. Using React.memo will conditionally re-render an entire component depending on the props passed in and what you choose should cause a render vs what shouldn’t.

If these items were not applicable or achievable for your use case then it might be worth using useMemo or useCallback.

Further reading

If you want more information on useMemo and useCallback here are some interesting reads:

Just a quick recap before we finish off

  • useMemo and useCallback are both react hooks that use memoization.
  • Memoization can be thought of as caching or remembering something.
  • useMemo will remember/memoize the value returned from your function.
  • useCallback will remember/memoize your function.

I hope you enjoyed this article and have found it useful!

Will

Image icon made by Freepik from www.flaticon.com