Custom Hooks — React

Bayangin 5 komponen di app kamu masing-masing pake pattern: useState buat data, useState buat loading, useEffect buat fetch — copy-paste kode yang sama 5x. Effo

Bayangin 5 komponen di app kamu masing-masing pake pattern: useState buat data, useState buat loading, useEffect buat fetch — copy-paste kode yang sama 5x. Effort nge-update bug fix? 5 tempat. Buang ke library? Gak bisa — udah tercampur dengan UI. Solusi React: custom hook = ekstrak shared LOGIC ke fungsi yang dimulai dengan use, panggil dari komponen mana saja kayak hook bawaan. Reuse logika tanpa share UI. Pattern ini bikin codebase 10x lebih maintainable.

💡 Aturan emas: nama harus mulai dengan use. useToggle, useFetch, useLocalStorage, useDebounce. Bukan cuma convention — Linter (eslint-plugin-react-hooks) check rule "Hooks must be called in same order tiap render", dan deteksinya berdasar prefix use. Lupa prefix = aturan gak ke-enforce, bug subtle.

Custom Hook Pertama

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue)
  const toggle = () => setValue(v => !v)
  return [value, toggle]
}

// Pemakaian — clean banget
function App() {
  const [isOpen, toggleOpen] = useToggle(false)
  return (
    <div>
      <button onClick={toggleOpen}>{isOpen ? "Tutup" : "Buka"}</button>
      {isOpen && <p>Konten tersembunyi!</p>}
    </div>
  )
}

function Modal() {
  const [isVisible, toggleVisible] = useToggle()  // reuse di komponen lain
  // ...
}

Custom Hook untuk Fetch Data

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

  useEffect(() => {
    let cancelled = false
    setLoading(true)

    fetch(url)
      .then(r => r.json())
      .then(d => { if (!cancelled) setData(d) })
      .catch(e => { if (!cancelled) setError(e.message) })
      .finally(() => { if (!cancelled) setLoading(false) })

    return () => { cancelled = true }
  }, [url])

  return { data, loading, error }
}

// Pemakaian — 1 baris instead of 20
function Users() {
  const { data, loading, error } = useFetch("/api/users")

  if (loading) return <Spinner />
  if (error) return <Error message={error} />
  return <UserList users={data} />
}

Custom Hook + LocalStorage

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key)
    return stored ? JSON.parse(stored) : initialValue
  })

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

  return [value, setValue]
}

// Pemakaian
function Settings() {
  const [theme, setTheme] = useLocalStorage("theme", "light")
  // theme persist across reload + tab close, automatically
}

Aturan Custom Hook

  1. Nama mulai dengan use (useToggle, BUKAN toggle)
  2. Hanya panggil hook di top level — gak boleh di if/loop/condition
  3. Hanya panggil dari React component atau hook lain — gak boleh dari util biasa
  4. Bisa panggil hook lainuseState, useEffect, atau hook custom lain

🎭 Analogi sehari-hari

Custom hook itu kayak resep mix bumbu rahasia warung soto. Dulu tiap koki bikin sendiri (bawang putih, ketumbar, lengkuas, takarannya beda-beda) — rasanya gak konsisten, ngabisin waktu, kalo koki pindah ilmu ilang. Sekarang: bumbu mix dijadi paket yang siap pake (useMixedSpices()), tinggal taro di tiap masakan, rasa konsisten, koki lain bisa pake. Plus pas perlu ubah resep (bug fix), cukup ubah di paket bumbu — semua masakan otomatis pake versi baru. Custom hook sama: extract logic kompleks (state + effect + cleanup) jadi paket reusable, panggil di komponen mana pun.

⚠️ Jebakan yang sering ditemui

🎯 Bikin custom hook atau function biasa?

  • Pakai useState/useEffect/hook lain di dalam → custom hook (use...)
  • Logic murni (no state, no effect) → function biasa
  • Cuma format/transform data → utility function
  • Akses DOM, browser API stateful → biasanya custom hook
  • Wrap untuk API library (Firebase, Supabase) → custom hook (manage subscription state)
  • Logic share antar 2-3 komponen + ada state → custom hook
  • Logic share antar 2-3 komponen + STATELESS → function biasa

Library populer custom hook: react-use, usehooks-ts — referensi pattern yang udah teruji.

TL;DR: Custom hook = ekstrak shared logic ke fungsi yang start dengan use, panggil dari komponen mana saja. Reuse stateful logic tanpa duplicate code. Wajib prefix use (lint rule). Tunduk Rules of Hooks (top-level call only). Common pattern: useToggle, useFetch, useLocalStorage, useDebounce. Return signature: object lebih clean dari array. Library populer: react-use, usehooks-ts. JANGAN over-extract — pattern muncul 2-3x baru extract.

Yang akan kamu pelajari