0
0
ReactHow-ToBeginner · 4 min read

How to Create useFetch Hook in React for Data Fetching

To create a useFetch hook in React, define a function that uses useState and useEffect to fetch data from a URL, manage loading and error states, and return these states. This hook lets components reuse data fetching logic cleanly and reactively.
📐

Syntax

The useFetch hook takes a URL string as input and returns an object with data, loading, and error states. It uses useState to store these states and useEffect to trigger the fetch when the URL changes.

  • url: The API endpoint to fetch data from.
  • data: The fetched data or null initially.
  • loading: Boolean indicating if the fetch is in progress.
  • error: Any error caught during fetch or null.
javascript
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    if (!url) return;
    setLoading(true);
    setError(null);

    fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then((json) => {
        setData(json);
        setLoading(false);
      })
      .catch((err) => {
        setError(err.message);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}
💻

Example

This example shows how to use the useFetch hook to get user data from a public API and display loading, error, or the user list.

javascript
import React from 'react';

function useFetch(url) {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(null);

  React.useEffect(() => {
    if (!url) return;
    setLoading(true);
    setError(null);

    fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then((json) => {
        setData(json);
        setLoading(false);
      })
      .catch((err) => {
        setError(err.message);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}

export default function UserList() {
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');

  if (loading) return <p>Loading users...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {data.map(user => (
        <li key={user.id}>{user.name} ({user.email})</li>
      ))}
    </ul>
  );
}
Output
<ul><li>Leanne Graham (Sincere@april.biz)</li><li>Ervin Howell (Shanna@melissa.tv)</li><li>Clementine Bauch (Nathan@yesenia.net)</li><li>Patricia Lebsack (Julianne.OConner@kory.org)</li><li>Chelsey Dietrich (Lucio_Hettinger@annie.ca)</li><li>Mrs. Dennis Schulist (Karley_Dach@jasper.info)</li><li>Kurtis Weissnat (Telly.Hoeger@billy.biz)</li><li>Nicholas Runolfsdottir V (Sherwood@rosamond.me)</li><li>Glenna Reichert (Chaim_McDermott@dana.io)</li><li>Clementina DuBuque (Rey.Padberg@karina.biz)</li></ul>
⚠️

Common Pitfalls

Common mistakes when creating useFetch include not handling cleanup on component unmount, causing memory leaks, or not resetting states when URL changes. Also, forgetting to check response.ok can lead to silent failures.

Always reset loading and error states before fetching new data to avoid stale UI states.

javascript
import { useState, useEffect } from 'react';

// Wrong: No cleanup and no error check
function useFetchWrong(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(json => {
        setData(json);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}

// Right: Includes cleanup and error check
function useFetchRight(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!url) return;
    let isMounted = true;
    setLoading(true);
    setError(null);

    fetch(url)
      .then(response => {
        if (!response.ok) throw new Error('Network response was not ok');
        return response.json();
      })
      .then(json => {
        if (isMounted) {
          setData(json);
          setLoading(false);
        }
      })
      .catch(err => {
        if (isMounted) {
          setError(err.message);
          setLoading(false);
        }
      });

    return () => {
      isMounted = false;
    };
  }, [url]);

  return { data, loading, error };
}
📊

Quick Reference

useFetch Hook Cheat Sheet:

  • url: API endpoint string
  • data: fetched data or null
  • loading: true while fetching
  • error: error message or null
  • Use useEffect to trigger fetch on URL change
  • Always check response.ok before parsing JSON
  • Handle cleanup to avoid setting state on unmounted components

Key Takeaways

Create useFetch with useState and useEffect to manage data, loading, and error states.
Always check response.ok to handle HTTP errors properly.
Reset loading and error states before each fetch to keep UI accurate.
Use cleanup in useEffect to prevent memory leaks when components unmount.
Return an object with data, loading, and error for easy use in components.