Skip to content
Advanced React Patterns

Lesson 2 of 6 · 25 min

x
2/6

Lesson position in the course — not completion. Use Mark Complete to track finished lessons (saved in this browser).

Custom Hooks & Encapsulated State Machines

Custom hooks extract stateful logic out of component rendering functions into reusable, testable functions. A custom hook is simply a JavaScript function whose name starts with use and which may call other React hooks (useState, useEffect, useRef).

When designing custom hooks, expose a clean contract: return only the necessary state values and action callbacks the consumer needs. To prevent infinite render loops when custom hook callbacks are passed to downstream useEffect dependency arrays, memoize returned functions with useCallback.

Before
Duplicated Fetching Logic inside Component
1// ❌ Duplicated state logic directly inside UI component2export function UserList() {3  const [data, setData] = useState(null);4  const [loading, setLoading] = useState(true);5  useEffect(() => {6    fetch('/api/users').then(r => r.json()).then(d => { setData(d); setLoading(false); });7  }, []);8  // ...9}
After
Encapsulated Custom Hook (`useFetch`)
1// ✅ Reusable custom hook with clean state contract2function useFetch<T>(url: string) {3  const [data, setData] = useState<T | null>(null);4  const [loading, setLoading] = useState(true);5 6  useEffect(() => {7    let isMounted = true;8    fetch(url).then(r => r.json()).then(d => {9      if (isMounted) { setData(d); setLoading(false); }10    });11    return () => { isMounted = false; };12  }, [url]);13 14  return { data, loading };15}

Exercise

Build a useLocalStorage<T>(key: string, initialValue: T) hook that synchronizes state with local storage.

Check your understanding

  • Why must custom hook names start with 'use'?Show answer

    Answer

    React's linter plugins use the 'use' prefix to enforce the Rules of Hooks (e.g., not calling hooks inside loops or conditions).
  • What is a stale closure in custom hooks?Show answer

    Answer

    When a callback captures variables from a past render because dependency arrays were omitted or left incomplete.
Previous

Progress is saved in this browser.

Next Lesson