Sunday, 8 December 2024

Understanding the Execution Order of useEffect and State Return in React with Async Operations

In modern React development, handling asynchronous data fetching is a common task. React's useEffect hook plays a key role in managing side effects, including making network requests. However, many developers encounter confusion regarding how the useEffect hook works in conjunction with state updates, particularly when dealing with asynchronous operations like API calls.

In this blog post, we'll explore how the useEffect hook operates, the sequence of events in asynchronous code execution, and how state is returned from custom hooks when performing data fetching. Specifically, we'll understand how React components re-render when state changes, and the timing of those state updates compared to the execution of async operations.

 

Follow below step-by-step procedure and build an 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 ‘fetch-user-details’

 

Project structure looks like below.

 


Step 2: Create hooks folder in src folder and define fetchUserDetails hook.

 

fetchUserDetails.js

import { useState, useEffect } from "react";

const fetchUserDetails = (userId) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      console.log(`Fetching user details for ${userId}`);
      setLoading(true);
      setError(null);

      try {
        console.log(`Adding a delay fo 2 seconds`);
        // Simulate a delay using setTimeout
        await new Promise((resolve) => setTimeout(resolve, 2000)); // 2-second delay

        const response = await fetch(
          `https://jsonplaceholder.typicode.com/users/${userId}`
        );

        if (!response.ok) {
          throw new Error("Failed to fetch user data");
        }

        const result = await response.json();
        console.log(`Setting the user details for the id ${userId}`);
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [userId]);

  return { data, loading, error };
};

export default fetchUserDetails;

Step 3: Update App.jsx like below.

 

App.jsx

import React, { useState } from "react";
import fetchUserDetails from "./hooks/fetchUserDetails";

const App = () => {
  const [userId, setUserId] = useState(1); // Default user ID is 1
  const { data, loading, error } = fetchUserDetails(userId);

  return (
    <div>
      <h1>User Details</h1>
      <div>
        <label htmlFor="userId">User ID: </label>
        <input id="userId" type="text" placeholder="1" />
        &nbsp;&nbsp;
        <button
          onClick={(e) => {
            setUserId(document.getElementById("userId").value);
          }}
        >
          Fetch Details
        </button>
      </div>

      {loading ? (
        <p>Loading...</p>
      ) : error || data === undefined ? (
        <p>
          <p style={{ color: "red" }}>Error: {error}</p>
        </p>
      ) : (
        <div>
          <h2>{data.name}</h2>
          <p>Email: {data.email}</p>
          <p>Phone: {data.phone}</p>
          <p>Website: {data.website}</p>
        </div>
      )}
    </div>
  );
};

export default App;

Step 4: Update main.jsx like below.

 

main.jsx

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.jsx";

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

Step 5: Execute below commands to run the application.

  cd fetch-user-details
  npm install
  npm run dev

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

 


In the console, you can see below messages.

 


Just enter other id (for ex: 2) and click on Fetch Details button to fetch user details.

 

How React’s Re-rendering Works for This Program?

In this program, the App component fetches user details based on the user ID input using the fetchUserDetails custom hook. The hook triggers asynchronous data fetching and handles states like loading, error, and data.

 

1. Initial Render of the App Component:

When the App component mounts, the initial state is set with userId as 1.

 

The fetchUserDetails hook is invoked with userId = 1.

 

1.1 Flow in fetchUserDetails

The initial state values for data, loading, and error are set using useState.

·      data = null

·      loading = true

·      error = null

 

The useEffect hook runs on mount because userId is part of its dependency array.

·      fetchData is called, which Logs "Fetching user details for 1." Sets loading = true and error = null (though loading is already true).

·      Starts a 2-second delay using setTimeout.

·      After the timeout, it fetches user details from the API and updates the state with the fetched data (setData(result)).

 

1.2 Rendering Output in App

Since loading is true initially, the App component renders the Loading... message.

 

2. Effect After Fetch Completes:

After the 2-second delay, the fetch request completes, and the following happens:

 

·      State Update: The data is successfully fetched and passed to setData. Then, loading is set to false.

 

·      This causes a re-render of the App component because fetchUserDetails updates the state (data, loading, or error). React re-renders whenever a component's state changes.

 

What happen when you fetch the user details with new id?

When the user changes the userId in the input field and clicks "Fetch Details"

 

·      State Update in App: The setUserId() function is called with the new userId entered by the user. This causes the App component to re-render with the updated userId.

·      Re-triggering the Fetch: Since userId is a dependency of useEffect in fetchUserDetails, the effect re-runs with the new userId. This initiates another fetch operation for the new user details.

The same process of setting loading = true, fetching data, and updating the state happens again, triggering subsequent re-renders of the App component.

 

You can download this application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment