← Back to the Build Your Homepage series
βš›οΈ
EPISODE 05
Extracting logic Β· naming rules Β· composition

Custom Hooks

Move repeated stateful logic into reusable hook functions. Learn the naming rules, common patterns (useToggle, useFetch, useLocalStorage), and composition.

custom hooksreuseuseLocalStorageuseFetch
Duration
⏱ About 2 hours
Level
πŸ“Š Intermediate
Prerequisite
🎯 react-02
OUTCOME
Build at least three reusable hooks used across components

What you'll learn

  • 1Name custom hooks with the use prefix
  • 2Return a tuple or object from a hook
  • 3Compose hooks within hooks
  • 4Write a useLocalStorage and useFetch

1. useToggle

tsx
function useToggle(initial = false): [boolean, () => void] {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn(v => !v), []);
  return [on, toggle];
}

// Use
const [isOpen, toggleOpen] = useToggle(false);

2. useLocalStorage

tsx
function useLocalStorage<T>(key: string, initial: T) {
  const [value, setValue] = useState<T>(() => {
    try {
      const raw = localStorage.getItem(key);
      return raw !== null ? JSON.parse(raw) as T : initial;
    } catch { return initial; }
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

3. useFetch (Simple Version)

tsx
function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [error, setError] = useState<Error | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;
    setLoading(true); setError(null);
    fetch(url)
      .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
      .then(d => { if (!cancelled) setData(d as T); })
      .catch(e => { if (!cancelled) setError(e); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [url]);

  return { data, error, loading };
}
πŸ’‘

For production apps, prefer TanStack Query or SWR β€” they handle caching, retries, and revalidation for you.

4. Naming Rules

  • Must start with "use" β€” that's how React detects a hook
  • Hooks can only call other hooks at the top level (no conditional calls)
  • Hooks can only be called from React function components or other hooks
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub β†—