DEV Community

Ujwal Kumar
Ujwal Kumar

Posted on

useMemo vs useCallback

useMemo and useCallback are two of the react hooks that help us in optimizing the performance of our application whenever components are re-rendered. They can be very useful in case you want to prevent some expensive computations from happening in your application (eg. an api call to the server that does some expensive calculations).

In this article, we will take a look on how to use these two hooks, what kind of problem do they solve and the difference between the two.

useMemo

The useMemo hook is used to return a memoized value. Memoization is a popular concept that refers to returning or reusing a cached value instead of computing it every time given the parameters used to calculate the value have not changed from the previous computation.

The official syntax of useMemo is useMemo(expensiveFunction, [dependencies]). From the syntax we can see that useMemo takes in two arguments. First argument is the expensive function call that we want to avoid while re-render of our component. This function should be pure, cannot take any arguments and the result of this function could be of any type. The second argument is a list of dependencies.

The function that is passed to useMemo is called during first time render of our component. Subsequent re-runs would depend on the list of dependencies that have been passed to the useMemo hook. If the dependencies have not changed, the old value is reused otherwise new value will be calculated. If no dependencies are passed, the expensive function will be called on each render.

Example

import { useMemo } from 'react';

function UserList({ users, profile }) {
  const activeUsers = useMemo(() => filterActiveUsers(users, profile), [users]);
}
Enter fullscreen mode Exit fullscreen mode

In the above example, we have received users as a prop inside our component. Let us say that filterActiveUsers is a very expensive method call that takes in too long time to give a result. If we want to stop the expensive method to re run everytime our UserList component is rendered, we can use useMemo to stop this function from running the next time given that the dependencies (users and profile in this case) do not change.

useCallback

The useCallback is used to return a memoized version of a callback. It's typical use case is when a child component expects a callback from a parent component and we want to prevent unnecessary re-renders of a child component.

The syntax of useCallback is useCallback(function, [dependencies]). As we can see, it is very similar to useMemo as in they both take a function as a first argument and takes in a list of dependencies as the second argument. The function definition that we pass in as the first parameter is cached and it can also take in some arguments which can be used for our business logic.

React returns (not call) the function that is passed as an argument to the useCallback hook on first render. The next time when this component re-renders, react checks the dependency list and will only compute the definition if the list of dependencies have changed from the last render.

Example

import { useCallback } from 'react';

export default function Cart({ productId, userId, orderDetails }) {
  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + productId + '/buy', {
      userId,
      orderDetails,
    });
  }, [productId, userId, orderDetails]);

return (
    <div>
      <Shipping onSubmit={handleSubmit} />
    </div>
  );

Enter fullscreen mode Exit fullscreen mode

In the above example, useCallback returns a function that can take in orderDetails as an argument. The function definition is stored inside handleSubmit. This function can in turn be passed to the child component (Shipping). The child component can then use this function. The function definition is cached and will only be recomputed in case the dependencies change on subsequent re-renders.

Be careful! Do not overuse these methods!

While these two hooks provide a solution to a real problem, we should be careful that we don't use them at the wrong place or overuse them.

For instance, there is no need to memoize a function that’s doing some basic calculation. We should only use useMemo when we know that a function will be very expensive either in terms of time of computation or in terms of the usage of resources. The results of useMemo gets stored in memory and can stay their for long time which in the long run may harm the performance of your application.

With useCallback as well there could be similar problems that we could create if we start caching simple function definitions. As these functions will stay in memory for longer periods of time and will not get garbage collected which in turn will again harm the performance of your application.

Key differences between useMemo and useCallback

  • useMemo is used to store the return value of a function in memory whereas useCallback is used to store the definition of a function.
  • The function passed to useMemo cannot take any arguments whereas the function in the useCallback hook can take parameters.
  • The function passed to useMemo is called on component render and it's returned value is cached in memory whereas useCallback just returns the function you passed to it but does not call it on component render.

Top comments (3)

Collapse
 
nielsabildgaard profile image
Niels Abildgaard

Thanks! Honestly, I always had trouble wrapping my head around why useCallback would be useful, and your example made it finally make sense to me!

Interestingly, I keep seeing this advice, that you also write:

For instance, there is no need to memoize a function that’s doing some basic calculation. We should only use useMemo when we know that a function will be very expensive either in terms of time of computation or in terms of the usage of resources.

I ran some tests on this, and could find no case where memoizing a (pure) component doesn't pay off. I assume the same is the case for pretty much any kind of calculation, too. That is, many more calculations make sense to memoize than we think!

Generally, stopping calculations from being in every render loop is good. The overhead of storing something in an in-memory cache really isn't large.

Collapse
 
ujwalkumar1995 profile image
Ujwal Kumar • Edited

Hey @nielsabildgaard
I am glad that you found this useful.
Just want to summarize couple of important things regarding the use of useMemo.
The initial render could be slightly slower with useMemo as the application is doing additional work. Also to quote the official react docs:

In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.

So we need to be careful while using it as we may unintentionally be doing more harm than good.

Collapse
 
nielsabildgaard profile image
Niels Abildgaard

I don't think they're saying to be careful - just that everything should work without it as well :-)

What I measured, in the experiments I mentioned above, was the difference in initial renders. It was unmeasurable even when memoizing every component at every level of abstraction of a complex application. As in, the actual measurable difference was less than random noise across several runs.

What I was commenting on was exactly the myth of "initial render could be slightly slower" --- it's not backed up by the numbers. The caching mechanism is incredibly fast.