Monday, 2 December 2024

Lazy Initialization in React's useState Hook: Boosting Performance with Functional Initial State

In this post, I am going to explain an advanced feature of React's useState hook—lazy initialization. When using useState, many developers pass a simple initial value, such as a string or number. However, when initializing state with complex computations, there's a more efficient way,  passing a function as the initial state argument. Known as "lazy initialization," this technique allows React to execute the function only once, during the first render.

By exploring this approach, we’ll understand how it helps to optimize component performance and when to consider using it.

 

Signature

const [state, setState] = useState(initialState);

 

·      state: The current state value.

·      setState: A function that allows you to update the state. When called, it queues a re-render of the component with the new state value.

·       initialState: The initial value of the state. It can be:

·      A direct value (e.g., a number, string, object).

·      A function that returns the initial value (for lazy initialization).

 

Example

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  let randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
  console.log(`randomNumber : ${randomNumber}`);
  return randomNumber;
}

const [count, setCount] = useState(() => {
  return getRandomInt(10, 100);
});

 

In this snippet, the useState hook uses a lazy initialization technique by taking a function as its argument. Lazy initialization with useState means the initial value of count is set by calling the function only once, when the component first renders.

 

Here’s how it works in this example.

·       Function Argument in useState: By passing a function (() => { return getRandomInt(10, 100); }) to useState, React only calls this function when it first needs to initialize count.

·       Benefit: Using a function for lazy initialization is beneficial when the initial state calculation (like generating a random number) is complex or involves performance-costly operations. By deferring this until the initial render, it avoids unnecessary recalculations on every re-render, as useState won’t call this function again unless the component is remounted.

 

In this case, the getRandomInt(10, 100) function generates and logs a random integer between 10 and 100, assigning that value as the initial state for count. This value is then preserved through re-renders unless updated explicitly by calling setCount.

 

 

Follow below step-by-step procedure to build the working application.

 

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-state-lazy-initialization’.

 

Project structure looks like below.

 


 

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

Counter.jsx

import { useState } from "react";

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  let randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
  console.log(`randomNumber : ${randomNumber}`);
  return randomNumber;
}

export default function Counter() {
  const [count, setCount] = useState(() => {
    return getRandomInt(10, 100);
  });

  return (
    <div className="container">
      <div className="counterApp">
        <p id="counterInfo">Current Counter : {count}</p>

        <button
          id="incCounter"
          onClick={() => {
            setCount((prevValue) => prevValue + 1);
          }}
        >
          Increment By 1
        </button>
      </div>
    </div>
  );
}

Step 3: Create styles folder in src folder and define styles.css file.

 

styles.css

.container {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
}

.counterApp {
  width: 400px;
  padding: 20px;
  background-color: #ffffff;
  border-radius: 15px;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2); /* Larger shadow for depth */
  transition: transform 0.2s ease; /* Subtle scaling on hover */
}

.counterApp:hover {
  transform: scale(1.05); /* Slight zoom effect */
}

#counterInfo {
  font-size: 2em;
  color: #333;
  margin: 15px 0;
  padding: 20px;
  border: 2px solid #0072ff;
  border-radius: 10px;
  background-color: #f7f9fc;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Softer shadow for text area */
  text-align: center;
}

#incCounter {
  font-size: 1.5em;
  padding: 15px 25px;
  color: #ffffff;
  background-color: #0072ff;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Button shadow */
  transition: background-color 0.3s ease, transform 0.2s ease;
}

Step 4: Update src/main.jsx with below content.

 

main.jsx

import { createRoot } from "react-dom/client";
import Counter from "./components/Counter";
import "./styles/styles.css";

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

Step 5: Follow below steps to run the Application.

  cd use-state-lazy-initialization
  npm install
  npm run dev

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




Counter is initialized with some random value between 10 and 100, you can click on the button ‘Increment By 1’ to understand the behavior better.

 

You can download the Application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment