React Lesson 13: Custom Hooks

⚛️ React CourseLesson 13 of 15 · 87% complete

Custom hooks let you extract and reuse stateful logic between components. When you find yourself writing the same useState + useEffect pattern in multiple components, extract it to a custom hook.

Rules of Hooks

// 1. Only call hooks at the TOP LEVEL of a function
// (not inside conditions, loops, or nested functions)

// 2. Only call hooks inside React function components
// or other custom hooks

// 3. Custom hook names MUST start with "use"
// useLocalStorage, useFetch, useForm, useTheme

useFetch Hook

import { useState, useEffect } from "react";

// Custom hook
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then(r => { if (!r.ok) throw new Error(r.status); return r.json(); })
      .then(d => { setData(d); setLoading(false); })
      .catch(e => { setError(e.message); setLoading(false); });
  }, [url]);
  
  return { data, loading, error };
}

// Use it in any component!
function UserList() {
  const { data, loading, error } = useFetch("https://jsonplaceholder.typicode.com/users");
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;
  return <ul>{data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

function PostList() {
  const { data, loading } = useFetch("https://jsonplaceholder.typicode.com/posts");
  // same hook, different URL!

useLocalStorage Hook

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    try {
      const stored = localStorage.getItem(key);
      return stored ? JSON.parse(stored) : initialValue;
    } catch { return initialValue; }
  });
  
  const setStoredValue = (newValue) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  };
  
  return [value, setStoredValue];
}

// Usage
const [theme, setTheme] = useLocalStorage("theme", "dark");
// Now theme persists across page reloads!

🏋️ Practice Task

Build a useDebounce(value, delay) hook that delays updating a value. Use it in a search component: as user types, only fire the search after they stop typing for 300ms. This prevents an API call on every keystroke.

💡 Hint: In useDebounce: use useEffect + setTimeout. Return the debounced value. In component: const debouncedQuery = useDebounce(query, 300).

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *