useMemo and useCallback in ReactJS

useMemo and useCallback in ReactJS

If you've struggled to make sense of useMemo and useCallback, you're not alone!

In this article, we shall discuss what is the useMemo and useCallback hook. We will then understand the use cases of these hooks with the help of examples. Then we will discuss when to use both of them. We shall also compare it with each other.

What are useMemo and useCallback?

useCallback:-

useCallback is a react hook that returns a memorized callback when passed a function and a list of dependencies that set the parameters. It’s useful when a component is passing a callback to its child component in order to prevent rendering. It only changes the callback when one of its dependencies is changed.

useMemo:-

useMemo is very similar to useCallback. It accepts a function and a list of dependencies, but the difference between useMemo and useCallback is that useMemo returns the memoized value returned by the passed function. It only recalculates the value when one of the dependencies changes. It’s very useful if you want to avoid expensive calculations on every render when the returned value isn’t changing

image.png

Why do we need useMemo and useCallback

The answer is simple - memoization between re-renders. If a value or a function is wrapped in one of those hooks, react will cache it during the initial render, and return the reference to that saved value during consecutive renders. Without it, non-primitive values like arrays, objects, or functions, will be re-created from scratch on every re-render. memoization is useful when those values are compared. It’s just your normal javascript:

const a = { "test": 1 };
const b = { "test": 1'};

console.log(a === b); // will be false

const c = a; // "c" is just a reference to "a"

console.log(a === c); // will be true

Or, if closer to our typical React use case:

const Component = () => {
  const a = { test: 1 };

  useEffect(() => {
    // "a" will be compared between re-renders
  }, [a]);

  // the rest of the code
};

a value is a dependency of useEffect hook. On every re-render of Component, React will compare it with the previous value. a is an object defined within the Component, which means that on every re-render it will be re-created from scratch. Therefore a comparison of a “before re-render” with an “after re-render” will return false, and useEffect will be triggered on every re-render.

To avoid it, we can wrap the a value in useMemo hook:

const Component = () => {
  // preserving "a" reference between re-renders
  const a = useMemo(() => ({ test: 1 }), []);

  useEffect(() => {
    // this will be triggered only when "a" value actually changes
  }, [a]);

  // the rest of the code
};

Now useEffect will be triggered only when the a value actually changes (i.e. never in this implementation).

Exactly the same story with useCallback, only it’s more useful for memoizing functions:

const Component = () => {
  // preserving onClick function between re-renders
  const fetch = useCallback(() => {
    console.log('fetch some data here');
  }, []);

  useEffect(() => {
    // this will be triggered only when "fetch" value actually changes
    fetch();
  }, [fetch]);

  // the rest of the code
};

The most important thing to remember here is that both useMemo and useCallback are useful only during the re-renders phase. During the initial render, they are not only useless but even harmful: they make React do some additional work. This means that your app will become slightly slower during the initial render. And if your app has hundreds and hundreds of them everywhere, this slowing down can even be measurable.

useMemo and useCallback Difference

Despite seeming very similar, there are different use cases for each. You should wrap functions with useCallback when passing a function as a dependency to other hooks or wrapping a functional component in React.Memo() that accepts your method as a property. You can use useMemo when you are working on functions where the inputs gradually change, where data values aren’t large enough to cause memory issues, or if the parameters are large enough so that the cost of comparison doesn’t outweigh the use of the wrapper.

useCallback works well in instances where the code would otherwise be recompiled with every call. Memorizing the results can decrease the cost of calling functions over and over again when the inputs change gradually over time.

Summary

That was quite a lot of information to process, hope you found it useful and are now eager to review your apps and get rid of all the useless useMemo and useCallback that accidentally took over your code. Quick summary to solidify the knowledge before you go:

  • useCallback and useMemo are hooks that are useful only for consecutive renders (i.e. re-renders), for the initial render they are actually harmful
  • useCallback and useMemo for props don’t prevent re-renders by themselves. Only when every single prop and the component itself are memoized, then re-renders can be prevented. One single mistake and it all falls apart and makes those hooks useless. Remove them if you find them.
  • Remove useMemo around “native” javascript operations - compare to components updates those are invisible and just take additional memory and valuable time during the initial render

One small thing: considering how complicated and fragile all of this is, useMemo and useCallback for performance optimisations really should be your last resort. Try other performance optimisation techniques first. Take a look at those articles that describe some of those: