Lesson 27 +10 XP

Extracting Reusable Hook Logic

Extracting Reusable Hook Logic

Let's explore building a widely useful custom hook: useFetch, designed to encapsulate API fetching, loading states, and error handling into a single reusable unit.

Building a Reusable useFetch Hook

// src/hooks/useFetch.js
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    let isMounted = true;
    setLoading(true);

    fetch(url)
      .then(res => {
        if (!res.ok) throw new Error("HTTP Error! Status: " + res.status);
        return res.json();
      })
      .then(json => {
        if (isMounted) {
          setData(json);
          setError(null);
        }
      })
      .catch(err => {
        if (isMounted) setError(err.message);
      })
      .finally(() => {
        if (isMounted) setLoading(false);
      });

    return () => {
      isMounted = false; // Prevent updating state on unmounted components
    };
  }, [url]);

  return { data, loading, error };
}

Using useFetch Across Multiple Components

Now any component in your application can fetch data with a single line of code!

``jsx // Component 1: Product Showcase function ProductList() { const { data: products, loading, error } = useFetch('/api/products');

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

return ( <ul> {products.map(p => <li key={p.id}>{p.name} - ${p.price}</li>)} </ul> ); }

// Component 2: User Directory function UserDirectory() { const { data: users, loading } = useFetch('/api/users');

if (loading) return <p>Loading users...</p>;

return <div>Total Users: {users ? users.length : 0}</div>; }


## Summary & TL;DR

- Custom hooks hide complex lifecycle management behind simple return signatures (e.g. `{ data, loading, error }`).
- Returning objects or arrays from custom hooks provides flexible destructuring options for consumer components.
- Common custom hook patterns include `useFetch`, `useLocalStorage`, `useDebounce`, and `useForm`.