What is the difference between the useCallback and useMemo hooks?

What is the difference between the useCallback and useMemo hooks?

Today we’ll look at the useCallback and useMemo hooks in depth and compare them.

When discussing performance optimization in React, useCallback and use Memo hook come into the picture. These hooks are both used to optimize the app.

Let’s start with the useCallback hook

useCallback hook

useCallback caches a function between re-renders until its dependencies change.

You need to pass two things to useCallback:

  1. A function definition that you want to cache between re-renders.

  2. A list of dependencies including every value within your component that’s used inside your function.

syntax


const handleSubmit = useCallback(() => {

  }, [dependencies]);

import { useCallback } from 'react';

function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) => {
   console.log(orderDetails)
  }, [productId, referrer]);

Here, we defined the handleSubmit function, which will be created once the app renders. When the component ProductPage re-renders, useCallback checks to see if any of its dependencies have changed and if so, it will re-create the function; otherwise, it will not.

Let’s walk through an example to see when this is useful

import { useCallback } from 'react';
import Shipping from './Shipping'

function ProductPage({ productId, referrer, theme }) {

  const handleSubmit = useCallback((orderDetails) => {
    console.log(orderDetails)
  }, [productId, referrer]);


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

Here, we are passing handleSubmit as a prop to the Shipping component, and each time ProductPage component re-renders it’ll re-render all its children but handleSubmit will not re-created because it’s dependencies didn’t change.

you can tell Shipping to skip re-rendering when its props are the same as on last render by wrapping it in memo(we'll learn it in depth)

After wrapping shipping function inside useMemo hook, we are saying to the react that only re-render this component if any of its props change.

import { memo } from "react";

const Shipping = memo(function Shipping({ onSubmit }) {
  console.log("shipping");
  return (
    <div>
      <h1 onClick={() => onSubmit()}>Hello I'm SHIPPING</h1>
    </div>
  );
});

export default Shipping;

By wrapping handleSubmit in useCallback, you ensure that it’s the same function between the re-renders (until dependencies change). You don’t have to wrap a function in useCallback unless you do it for some specific reason. In this example, the reason is that you pass it to a component wrapped in memo, and this lets it skip re-rendering.

Should you add useCallback everywhere?

  1. You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.

  2. The function you’re passing is later used as a dependency of some Hook. For example, another function wrapped in useCallback depends on it, or you depend on this function from useEffect.

    useMemo Hook

    useMemo is a React Hook that lets you cache the result of a calculation between re-renders.

    const cachedValue = useMemo(calculateValue, dependencies)
    

    Let’s walk through an example to see when this is useful

    Skipping expensive recalculations

    To cache a calculation between re-renders, wrap it in a useMemo call at the top level of your component:

    useMemo caches a calculation result between re-renders until its dependencies change.

    You need to pass two things to useMemo:

    1. A calculation function that takes no arguments, like () =>, and returns what you wanted to calculate.

    2. A list of dependencies including every value within your component that’s used inside your calculation.

      import { useMemo } from 'react';
      
      function TodoList({ todos, tab, theme }) {
        const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
        // ...
      }
      

      On the initial render, the value you’ll get from useMemo will be the result of calling your calculation.

      on every render React will compare the dependencies of useMemo hook . If none of the dependencies have changed (compared with Object.is), useMemo will return the value you already calculated before. Otherwise, React will re-run your calculation and return the new value.

      Usually, this isn’t a problem because most calculations are very fast. However, if you’re filtering or transforming a large array, or doing some expensive computation, you might want to skip doing it again if data hasn’t changed. If both todos and tab are the same as they were during the last render, wrapping the calculation in useMemo like earlier lets you reuse visibleTodos you’ve already calculated before. This type of caching is called memoization.

Should you add useMemo everywhere?

Optimizing with useMemo is only valuable in a few cases:

  1. The calculation you’re putting in useMemo is noticeably slow, and its dependencies rarely change.

2. You pass it as a prop to a component wrapped in memo. You want to skip re-rendering if the value hasn’t changed. Memoization lets your component re-render only when dependencies aren’t the same.

3. The value you’re passing is later used as a dependency of some Hook. For example, maybe another useMemo calculation value depends on it. Or maybe you are depending on this value from useEffect.

Skipping re-rendering of components

as we done before in shipping component.

another way of wrapping a component in useMemo


function Shipping({ onSubmit }) {
  console.log("shipping");
  return (
    <div>
      <h1 onClick={() => onSubmit()}>Hello I'm SHIPPING</h1>
    </div>
  )};

import { useCallback } from 'react';
import Shipping from './Shipping'

function ProductPage({ productId, referrer, theme }) {

  const handleSubmit = useCallback((orderDetails) => {
    console.log(orderDetails)
  }, [productId, referrer]);


   const memoShipping = useMemo(() => <Shipping onSubmit={handleSubmit} />, [handleSubmit]);

   return (
    <div className={theme}>
     {memoShipping}
    </div>
  );

Memoizing a function

import { useCallback } from 'react';
import Shipping from './Shipping'

function ProductPage({ productId, referrer, theme }) {

  const handleSubmit = useMemo(() => {
      return (orderDetails) => {
          console.log(orderDetails)
        };
  }, [productId, referrer]);

   const memoShipping = useMemo(() => <Shipping onSubmit={handleSubmit} />, [handleSubmit]);

   return (
    <div className={theme}>
     {memoShipping}
    </div>
  );

To memoize a function with useMemo, your calculation function would have to return another function.

Let’s see the diffrence between useCallback and useMemo hook

Wrap your functions into useCallback instead of useMemo to avoid having to write an extra nested function

//USE MEMO HOOK

const handleSubmit = useMemo(() => {
      return (orderDetails) => {
          console.log(orderDetails)
        };
  }, [productId, referrer]);

//USECALLBACK HOOK

const handleSubmit = useCallback((orderDetails) => {
    console.log(orderDetails)
  }, [productId, referrer]);

The two examples above are completely equivalent. The only benefit to useCallback is that it lets you avoid writing an extra nested function inside. It doesn’t do anything else. Read more about useCallback.

Hope you like it 🤗

Happy coding!

want to give suggestions:-

find me on LinkedIn Twitter

Email me at nidhisharma639593@gmail.com