Optimizing React Components: A Guide to useCallback and useMemo

dickinsontiwari - Oct 16 '23 - - Dev Community

Understanding the Difference Between useCallback and useMemo in React

In the world of React, performance optimization is a crucial aspect of building efficient applications. Two often-used tools for optimizing child components are the useCallback and useMemo hooks. These hooks allow you to cache data or functions, preventing unnecessary re-renders and improving the overall performance of your application. In this blog post, we will explore the differences between these two hooks, using real-life examples to illustrate when to use each.

The Purpose of useMemo and useCallback

Before diving into the differences, let's clarify the purpose of useMemo and useCallback.

useMemo: This hook is used to cache the result of calling a function. It's particularly helpful when you want to avoid recalculating a value on every render. The result is only recalculated if one or more of the specified dependencies change.

useCallback: In contrast, useCallback caches the function itself, rather than its result. It's perfect for preventing a function from being recreated on each render, ensuring that it remains consistent as long as the specified dependencies do not change.

Real-Life Example: ProductPage

Let's consider a real-life example involving a Product component. This component displays product information and a form for making a purchase. We'll use this example to illustrate when to use useMemo and when to use useCallback.

First we need to fetch data. I am using Upstash Redis as database. Lets define the home page.

import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

export const revalidate = 0; // disable cache

export default async function ProductPage({ productId }) {
  const productDetails = await redis.get(`product:${productId}`);

  // Parse the JSON data if your product details are stored as JSON in Redis
  const product = JSON.parse(productDetails);

  return (
    <div className="container mx-auto p-4">
      <main className="main">
        <h1 className="text-3xl font-bold">Product Details</h1>
          <div>
            <p className="text-lg mt-2">Product Name: {product.name}</p>
            <p className="text-lg">Price: ${product.price.toFixed(2)}</p>
            {/* Add more product details here */}
          </div>
          <Shipping product={product} referrer={referrer} />
      </main>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now lets define the shipping component.

// Shipping.tsx
"use client";
import { useMemo, useCallback } from 'react';
import ShippingForm from "@/components/shippingform"

function ShippingComponent({ product, referrer }) {
  const requirements = useMemo(() => {
    return computeRequirements(product);
  }, [product]);

  const handleSubmit = useCallback((orderDetails) => {
    post('/product/' + product.id + '/buy', {
      referrer,
      orderDetails,
    });
  }, [product.id, referrer]);

  return (
    <div>
    {/* Another Client Component */}
      <ShippingForm requirements={requirements} onSubmit={handleSubmit} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useMemo in Action

In this example, requirements is memoized using useMemo. It calculates the requirements based on the product. This is useful because computeRequirements can be a computationally expensive operation. By caching the result of computeRequirements(product), we ensure that the requirements object doesn't change unless the product changes. This prevents unnecessary re-renders of the ShippingForm component.

useCallback in Action

On the other hand, the handleSubmit function is cached using useCallback. This function is tied to the productId and referrer values, and we want to ensure that it doesn't get recreated every time the component re-renders. This is particularly important because handleSubmit is an event handler used when the user submits the form. By caching the function itself with useCallback, we make sure that the code won't run until the user actually submits the form, improving performance.

Simplifying useCallback

For those already familiar with useMemo, you can think of useCallback as a simplified version. In essence, useCallback is a wrapper around useMemo that caches a function.

function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}
Enter fullscreen mode Exit fullscreen mode

When to Choose useMemo or useCallback

In summary, you should choose useMemo when you want to cache the result of a function call and ensure it doesn't change unless specific dependencies change. On the other hand, opt for useCallback when you want to cache a function itself and prevent it from being recreated on each render, which is particularly useful for event handlers.

By understanding these differences and choosing the appropriate hook for your use case, you can significantly enhance the performance and efficiency of your React applications. Whether you're optimizing data or functions, useMemo and useCallback are powerful tools at your disposal to achieve that goal.

. . . . .
Terabox Video Player