Custom hooks

A custom hook is just a function whose name starts with use. That naming convention tells React it can call other hooks inside it. The pattern is how you extract repeated data-fetching, form, or state logic into a reusable piece that any component can call.

useBooks.ts
tsx
import { useEffect, useState } from 'react';

interface Book {
  id: number;
  title: string;
  author: string;
}

export function useBooks() {
  const [books, setBooks] = useState<Book[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();
    fetch('/api/books', { signal: controller.signal })
      .then((res) => {
        if (!res.ok) throw new Error('HTTP ' + res.status);
        return res.json();
      })
      .then(setBooks)
      .catch((err) => {
        if (err.name !== 'AbortError') setError(err.message);
      })
      .finally(() => setLoading(false));

    return () => controller.abort();
  }, []);

  return { books, loading, error };
}

A custom hook that wraps the fetch-and-state pattern.

BookList.tsx
tsx
import { useBooks } from './useBooks';

export function BookList() {
  const { books, loading, error } = useBooks();

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;
  return books.map((b) => <BookCard key={b.id} book={b} />);
}

Any component can now get books, loading, and error in one line.

Quiz: Quiz

Loading practiceโ€ฆ