Saturday, 4 January 2025

Optimizing Performance in React with the useCallback Hook

The useCallback hook in React is used to memorize functions, allowing you to "cache" a function instance and avoid unnecessary re-creations on re-renders.

Signature

const memoizedCallback = useCallback(callback, dependencies);

callback: This is the function you want to memoize. useCallback will return a memoized version of this function, meaning it won’t be recreated on each render unless its dependencies change.

 

dependencies: This is an array of values that the function depends on. The function will only be recreated if one or more of these dependencies change. If no dependencies are provided (i.e., an empty array []), the function will remain the same across all renders after the initial one.

 

Example

const memoziedFunction = useCallback(() => {
  localVar1++;
  console.log(`Memoized function : localVar : ${localVar1}`);
}, []);

Let’s build an application to understand custom hook better.

 

Step 1: Go to the post (Quickly Set Up a React Project with Vite: A Step-by-Step), and set up a React Project ‘use-callback-demo’

 

Project structure looks like below.

 


Step 2: Create components folder in src folder and define HelloWorld component.

 

HelloWorld.jsx

import { useCallback, useEffect, useState } from "react";

export default function HelloWorld() {
  const [counter, setCounter] = useState(0);
  let localVar1 = 0;
  let localVar2 = 0;

  const memoziedFunction = useCallback(() => {
    localVar1++;
    console.log(`Memoized function : localVar : ${localVar1}`);
  }, []);

  const regularFunction = () => {
    localVar2++;
    console.log(`Regular function : localVar : ${localVar2}`);
  };

  useEffect(() => {
    memoziedFunction();
    regularFunction();
  }, [counter]);

  return (
    <div>
      <button
        onClick={(event) => {
          setCounter((counter) => counter + 1);
        }}
      >
        Increment Counter
      </button>
    </div>
  );
}

Step 3: Update main.jsx with below content.

 

main.jsx

import { createRoot } from "react-dom/client";
import HelloWorld from "./components/HelloWorld";

createRoot(document.getElementById("root")).render(<HelloWorld />);

Step 4: Run the Application by executing following commands.

cd use-callback-demo
npm install
npm run dev

Open the url ‘http://localhost:5173/’ in browser, you will see below screen.



Go inspect tool -> Console tab, you can observe following messages.

Memoized function : localVar : 1 
Regular function : localVar : 1
Memoized function : localVar : 2 
Regular function : localVar : 1
Memoized function : localVar : 3 
Regular function : localVar : 1
Memoized function : localVar : 4 
Regular function : localVar : 1

Memoized Function Behavior (memoziedFunction)

Since useCallback is used with an empty dependency array ([]), memoziedFunction is created only once and retains its reference across all renders. localVar1 behaves like a persistent closure within memoziedFunction. Each time memoziedFunction is called, it increments localVar1 from its previous value, rather than resetting to 0 on each render.

 

This is why localVar1 keeps increasing with each useEffect call. It retains its incremented state within the useCallback closure, even though it’s technically a local variable.

 

Regular Function Behavior (regularFunction)

regularFunction is recreated on every render, so localVar2 resets to 0 each time regularFunction is redefined.

 

Each time regularFunction is called, localVar2 increments from 0 to 1, and then logs this value, giving the impression of a reset. This happens because the function doesn’t have a stable reference, so it doesn’t retain any previous state.

  

Previous                                                    Next                                                    Home

No comments:

Post a Comment