Monday, 6 January 2025

Building a Custom useFetch Hook in React for Effecient Data Fetching

Fetching data from APIs is a common need in React applications, but handling it within components can often become repetitive and cluttered. Using a custom hook like useFetch can simplify the data retrieval, especially for reusable components or cases with complex error and loading states.

In this post, I will walk you through creating a useFetch hook that can handle asynchronous requests, track loading status, and manage errors—all in a reusable, efficient way.

 

What are custom hooks?
Custom hooks in React are functions that start with "use" (like useFetch). They allow you to reuse logic that you’d otherwise have to repeat in different components. So instead of writing the same data-fetching code every time you need it, you can just use the useFetch hook whenever you want to get data from an API.

 

Benefits of using useFetch

·       Code Reuse: You write your data-fetching code once, and you can use it in multiple components.

·       Cleaner Components: With useFetch, your components only have to focus on displaying data. The data-fetching logic is handled in the hook, making components easier to read and maintain.

 

By creating a custom hook, you’re keeping your code neat and saving time.

 

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-fetch-hook-demo’.

 

Project structure looks like below.

 


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

 

useFetch.jsx

import { useEffect, useState } from "react";

export default function useFetch(url) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  async function fetchData() {
    try {
      setLoading(true);
      console.log(`Fetching Data from the url ${url}`);

      const response = await fetch(url);

      if (!response.ok) {
        setError("Response Is Not OK....");
        return;
      }

      const data = await response.json();
      console.log(data);
      setData(data);
    } catch (error) {
      console.error(error);
      setError(error);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    console.log("Triggering the url " + url);
    fetchData();
  }, [url]);

  return { loading, error, data };
}

This useFetch hook is designed to fetch data from a given url using the fetch API. It provides a clean and reusable way to manage loading state, error handling, and the fetched data in React components.

 

State Management

The hook manages three pieces of state using React’s useState:

1.   loading: Tracks whether the data is still being fetched (true/false).

2.   error: Stores any error message that might occur during the fetch process.

3.   data: Holds the fetched data once the request is successful.

 

These states are initialized as follows:

·       loading starts as false (i.e., not loading).

·       error starts as null (i.e., no error by default).

·       data starts as null (i.e., no data).

 

Fetching Logic (fetchData)

The fetchData function is an async function that performs the actual data fetching using the fetch API:

·       Set Loading to true: Before starting the request, the loading state is set to true to indicate that the fetch process is in progress.

·       Perform the Fetch: The fetch function is called with the provided url.

o   If the response is not okay (response.ok is false), it sets an error state with a generic error message.

o   If the response is okay, the JSON data is extracted and stored in the data state.

·       Error Handling: If any error occurs during the fetch (e.g., network issues), it is caught in the catch block and stored in the error state.

·       Set Loading to false: Regardless of success or failure, loading is set to false in the finally block to indicate that the fetch process is complete.

 

Effect Hook (useEffect)

The useEffect hook is used to trigger the fetchData function whenever the url changes:

·       It logs the triggering of the URL and then calls fetchData to initiate the data fetching process.

·       The dependency array [url] ensures that the fetch operation is re-triggered only when the url changes. If the url remains the same, the hook does not re-run.

 

Return Values

The hook returns an object with the following properties:

·       loading: The current loading state.

·       error: Any error that occurred during the fetch process.

·       data: The data retrieved from the fetch request.

This allows a React component that uses useFetch to easily manage and display the loading state, any errors, and the fetched data.

 

Step 4: Create components folder in src folder and define ImageLoader component.

 

ImageLoader.jsx

import { useEffect, useState, useRef } from "react";
import useFetch from "../hooks/useFetch";

const urlToFetch = "https://picsum.photos/v2/list?page={PAGE_NUMBER}&limit=10";

export default function ImageLoader() {
  const [fullImages, setFullImages] = useState([]);
  const [pageNumber, setPageNumber] = useState(1);
  const url = urlToFetch.replace("{PAGE_NUMBER}", pageNumber);
  const { loading, error, data } = useFetch(url);

  // Update fullImages when new data is fetched
  useEffect(() => {
    if (data && data.length > 0) {
      setFullImages((prevImages) => [...prevImages, ...data]);
    }
  }, [data]); // Only run this effect when data changes

  return (
    <div>
      {loading && <div className="loading">Loading Is In Progress.....</div>}

      {error && (
        <div className="error">Error Occurred While Fetching Images....</div>
      )}

      {fullImages.length > 0 && (
        <div className="imagesContainer">
          {fullImages.map((image, index) => (
            <img
              src={image.download_url}
              alt={image.id}
              key={index}
              width="200"
              height="200"
            />
          ))}
        </div>
      )}

      <button
        onClick={() => {
          setPageNumber((prevPageNumber) => prevPageNumber + 1);
        }}
      >
        Load More Images
      </button>
    </div>
  );
}

Step 5: Go to the project root folder and execute following commands.

npm install
npm run dev

Click on ‘Load More Images’ button to load more images.

You can download the Application from this link.



Previous                                                    Next                                                    Home

No comments:

Post a Comment