/

August 5, 2024

Part-2 Unlocking the Power of React Hooks: A Comprehensive Guide to Mastering Modern Frontend Development

6. useMemo Hook:

Introduction to useMemo Hook:

The useMemo Hook in React is used to memoize expensive calculations so that they are only computed when necessary. It is similar to useCallback, but instead of memoizing callback functions, it memoizes the result of a function computation. This can be useful for optimizing performance in scenarios where a calculation is computationally expensive and does not need to be re-computed on every render.

Practical application: Memoizing expensive calculations:

When rendering components, React re-evaluates all expressions in the component’s render function on every render. In cases where a computation is expensive, such as complex data transformations or heavy mathematical operations, this can lead to performance bottlenecks. By memoizing the result of such calculations with useMemo, you can ensure that the computation is performed only when its dependencies change, optimizing performance by avoiding unnecessary re-calculations.

Example: Computing derived data and optimizing performance:

Let’s consider an example where a component displays a list of numbers, and we want to compute the sum of all numbers in the list. We’ll use useMemo to memoize the sum calculation so that it is only re-computed when the list of numbers changes.

import React, { useState, useMemo } from ‘react’;

function NumberList({ numbers }) {

  // Memoize the sum calculation using useMemo

  const sum = useMemo(() => {

    console.log(‘Calculating sum…’);

    return numbers.reduce((acc, curr) => acc + curr, 0);

  }, [numbers]); // Depend on numbers array to recompute sum when it changes

  return (

    <div>

      <h2>Number List</h2>

      <ul>

        {numbers.map((number, index) => (

          <li key={index}>{number}</li>

        ))}

      </ul>

      <p>Sum of Numbers: {sum}</p>

    </div>

  );

}

function App() {

  const [numbers, setNumbers] = useState([1, 2, 3, 4, 5]);

  const addRandomNumber = () => {

    setNumbers([…numbers, Math.floor(Math.random() * 10)]);

  };

  return (

    <div>

      <h1>Number List App</h1>

      <button onClick={addRandomNumber}>Add Random Number</button>

      <NumberList numbers={numbers} />

    </div>

  );

}

export default App;

In this example:

We define a NumberList component that receives a list of numbers as a prop.

Inside the NumberList component, we use useMemo to memoize the sum calculation of the numbers array. The sum is computed using the reduce method.

The useMemo Hook depends on the numbers array, so the sum calculation is re-computed only when the numbers array changes.

In the App component, we manage a state variable numbers that represents the list of numbers. We provide a button to add a random number to the list.

The NumberList component is rendered with the current numbers state, and the sum of numbers is displayed below the list.

By memoizing the sum calculation with useMemo, we optimize the performance of the NumberList component by avoiding unnecessary re-calculations of the sum when the list of numbers changes.

7. useRef Hook:

Introduction to useRef Hook:

The useRef Hook in React provides a way to create mutable references that persist across renders. Unlike state variables, changes to useRef values do not trigger re-renders. useRef is commonly used to reference DOM elements, store mutable values, or create references that persist for the entire lifecycle of a component.

Practical application: Referencing DOM elements and persisting values between renders:

useRef is particularly useful for interacting with the DOM, such as accessing or modifying DOM elements imperatively. It allows you to create a reference to a DOM element and access its properties or methods directly. Additionally, useRef can be used to store values that need to persist between renders without causing re-renders.

Example: Implementing a custom focus management hook:

Let’s create a custom hook called useFocus that manages the focus of an input element. This hook will use useRef to create a reference to the input element and allow us to programmatically focus on it.

import React, { useRef } from ‘react’;

// Custom hook to manage focus

function useFocus() {

  const inputRef = useRef(null);

  const focusInput = () => {

    inputRef.current.focus();

  };

  return inputRef;

}

function FocusInput() {

  const inputRef = useFocus();

  return (

    <div>

      <h2>Focus Input</h2>

      <input ref={inputRef} type=”text” />

      <button onClick={() => inputRef.current.focus()}>Focus Input</button>

    </div>

  );

}

export default FocusInput;

In this example:

We define a custom hook called useFocus that utilizes useRef to create a reference to an input element.

The useFocus hook returns the input element reference, allowing us to access and manipulate the input element imperatively.

In the FocusInput component, we use the useFocus hook to create a reference to the input element.

We render an input element and a button in the component. Clicking the button focuses on the input element using the imperative focus method.

By using useRef to create a reference to the input element, we can programmatically manage the focus of the input element without relying on state or causing unnecessary re-renders of the component. This demonstrates how useRef can be used to reference DOM elements and persist values between renders in a React component.

8. useTransition:

Introduction to the useTransition Hook:

Purpose: Introduced in React 18, the useTransition hook aims to enhance application responsiveness and perceived performance when dealing with state updates that might impact user-perceived smoothness, such as data fetching or complex state interactions.

Functionality: It enables you to mark certain state updates as “transitions,” indicating they can be deferred without hindering core UI interactions. Updates are queued and applied asynchronously under the hood, while the UI remains interactive.

Benefits:

Improved user experience: Prevents UI freezes during long-running updates, maintaining responsiveness.

Smoother animations: Can be used to create visually pleasing transitions that don’t block user input.

Optimized performance: Prioritizes critical updates first, ensuring a lag-free experience.

Enhanced code clarity: Separates urgent UI concerns from non-critical updates.


Practical Applications:

1. Creating Smooth Page Transitions:

Scenario: Navigating between pages in a React app typically involves loading new content, potentially causing delays that disrupt the user flow.

Solution:

Utilize useTransition to defer the fetching and rendering of new content until after the initial transition animation is complete.

This preserves the smooth visual effect while keeping the UI usable.

Use an animation library like Framer Motion or react-spring to create the transition.

2. Optimizing Data Fetches:

Scenario: Fetching data from APIs or internal data stores can introduce lag while the UI waits for the data.

Solution:

Mark the data fetching process as a useTransition update.

Display a loading indicator while the data is fetched in the background.

Once the data arrives, update the UI smoothly without blocking interactions.

3. Handling Large Form Submissions:

Scenario: Submitting lengthy forms can lead to UI freezes while data is validated and processed.

Solution:

Use useTransition to handle the validation and processing as a transition.

Disable the submit button immediately and display a loading indicator.

Upon successful submission or error handling, transition the UI back to its original state without affecting responsiveness.

Example:

import React, { useState, useTransition, useEffect } from ‘react’;

export default function App({ users }) {

  const [searchTerm, setSearchTerm] = useState(”);

  const [filtered, setFiltered] = useState();

  const [isPending, startTransition] = useTransition();

  const [uData, setuData] = useState({});

  useEffect(() => {

    const data = fetch(“http://192.168.1.70:5000/”);

    data.then((res) => res.json()).then((data) => {

      setFiltered(data?.data);

      setuData(data?.data);

    });

  },[])

  const handleChange = ({ target: { value } }) => {

    setSearchTerm(value);

    

      

      startTransition(() => {

        setFiltered(uData.filter((item) => item.name.includes(value)));

      });

   

  };

  isPending ? console.log(“Loading…”) : console.log(“Data loaded”);

  return (

    <div className=”container”>

      <div>

        {

          isPending ? (

            <div>Loading…</div>

          ) : (

            <p>

              {

                 uData?.length !== filtered?.length 

                 ? `${filtered.length} matches` 

                 : null

              } 

            </p>

          )

        }

      </div>

      <input 

        onChange={handleChange} 

        value={searchTerm} 

        type=”text”

        placeholder=”Type a name” />

      {

        isPending ? (

          <div>Loading…</div>

        ) : (

          <div className=”cards”>

            {filtered?.map((user,idx) => (

              <div className=”card” key={idx}>

                <div className=”profile”>

                  <img 

                    src={`https://i.pravatar.cc/150?img=${idx}`}              

                    alt=”Avatar” />

                </div>

                <div className=”body”>

                  <strong>{user.name}</strong>

                </div>

              </div>

            ))}

          </div>

        )}

    </div>

  );

}

9. useDeferredValue Hook:

Introduction to useDeferredValue:

Introduced in React 18, the useDeferredValue hook empowers you to optimize performance by deferring non-critical state updates that might impact rendering performance. It selectively prioritizes essential updates, ensuring a smooth and responsive user experience.

Key Functionality:

1. Identifies updates marked with useDeferredValue as non-critical.

2. Postpones their application until after critical, user-perceived UI updates have completed.

3. Renders the component with the previous value during the deferral period.

4. Once critical updates are finished, the component seamlessly re-renders with the deferred value.

Benefits:

Prevents UI freezes: Avoids rendering delays caused by computationally expensive computations or data fetching for secondary UI elements.

Enhances perceived performance: Creates a smoother user experience by prioritizing critical interactions.

Optimizes code readability: Separates urgent concerns from deferrable aspects, improving code clarity.

Practical Applications:

1. Deferring Complex Calculations:

Scenario: Displaying a list of items with a calculated score or rating based on complex data.

Solution:

Calculate scores outside the render function to avoid blocking the initial render.

Use useDeferredValue to defer displaying the calculated scores until after the initial list renders.

2. Optimizing Data Fetching:

Scenario: Rendering details or additional information after fetching data from an API.

Solution:

Display a loading indicator initially.

Use useDeferredValue to defer fetching and rendering additional data until the critical list renders.

3. Handling Large Datasets:

Scenario: Working with extensive datasets that might affect rendering performance.

Solution:

Render an initial subset of the data efficiently.

Use useDeferredValue to defer rendering the remaining data in the background.

10. useID:

Introduction to useId:

Introduced in React 18, the useId hook empowers you to generate unique identifiers (IDs) for HTML elements within your components.

These IDs are essential for accessibility purposes, allowing screen readers and assistive technologies to identify and navigate elements effectively.

It also simplifies handling form field associations with labels and improves styling capabilities.

Practical Applications:

1. Crafting Accessible Forms:

Unique IDs for form elements:

Use useId to provide unique IDs for input fields, textareas, and other form elements.

Associate these IDs with corresponding labels through the for attribute, ensuring screen readers correctly announce them.

Simplified label management:

Bind labels to their respective fields using the id and for attributes, avoiding manual ID management.

This enhances code clarity and simplifies maintenance.

2. Enhancing Styling Efficiency:

Targeted Styling:

Utilize IDs generated by useId to target specific elements with CSS, promoting modular and maintainable styles.

Avoid class-based styling that can become less specific and harder to manage in complex UIs.

Dynamic Content Management:

IDs created by useId are stable across renders, even for dynamically generated elements.

This allows reliable styling and interaction management for components with variable content.

3. Building Reusable Components:

Consistent IDs:

useId guarantees consistent ID generation even when a component renders multiple times with the same data.

This avoids conflicts and ensures proper accessibility and styling behavior.

Improved Testability:

Unique IDs simplify testing by enabling targeted interaction with specific elements in your components.

Example:

import React, { useState, useId } from ‘react’;

 

function App() {

  const nameId = useId();

  const emailId = useId();

 

  const [name, setName] = useState(”);

  const [email, setEmail] = useState(”);

 

  return (

    <form>

      <label htmlFor={`user-name${nameId}`}>Name:</label>

      <input

        id={`user-name${nameId}`}

        type=”text”

        value={name}

        onChange={(e) => setName(e.target.value)}

      />

      <label htmlFor={`user-email${emailId}`}>Email:</label>

      <input

        id={`user-email${emailId}`}

        type=”email”

        value={email}

        onChange={(e) => setEmail(e.target.value)}

      />

    </form>

  );

}

export default App;


Additional Considerations:

Avoid manually creating IDs, as they might conflict with IDs generated by other libraries or frameworks.

Use useId for elements that require unique IDs for accessibility or styling purposes.

Don’t rely on these IDs in client-side logic, as they might change on the server or across rehydrates.

By effectively utilizing useId, you can create accessible, modular, and well-styled React components that contribute to a robust and user-friendly web experience.


From the same category