Custom hook adalah function biasa yang namanya diawali use dan memanggil hook lain di dalamnya, misalnya useState. Dengan custom hook, logika yang memegang state (state plus cara mengubahnya) cukup ditulis sekali, lalu dipakai di banyak komponen. Mirip satu resep rendang yang dipakai dua dapur: resepnya sama, tapi tiap dapur memasak di pancinya sendiri. Kalau dapur A menambah garam, panci dapur B gak ikut asin.
🧪 Tebakan cepat: lampu Teras dan lampu Dapur sama-sama memanggil
useToggle(true). Kamu klik Toggle di Teras sampai jadi OFF. Apakah Dapur ikut OFF? Tebak dulu, jawabannya ada di bagian "Tiap panggilan punya state sendiri".
Logika yang sama ditulis berulang
Dua komponen ini sama-sama butuh nilai true atau false yang bisa dibalik:
function Lamp({ name, initial }) {
const [isOn, setIsOn] = useState(initial)
const toggle = () => setIsOn((v) => !v)
return (
<div>
<p>{name}: {isOn ? "ON" : "OFF"}</p>
<button onClick={toggle}>Toggle</button>
</div>
)
}
function Spoiler() {
const [isOpen, setIsOpen] = useState(false)
const toggle = () => setIsOpen((v) => !v)
return (
<div>
<button onClick={toggle}>{isOpen ? "Tutup" : "Lihat jawaban"}</button>
{isOpen && <p>Jawabannya: custom hook!</p>}
</div>
)
}
Dua baris pertama di tiap komponen isinya sama, cuma beda nama. Ada bug di cara membaliknya? Kamu harus membetulkannya di setiap komponen yang menyalin baris itu.
Pindahkan ke custom hook
import { useState } from "react"
function useToggle(initial = false) {
const [value, setValue] = useState(initial)
const toggle = () => setValue((v) => !v)
return [value, toggle]
}
function Lamp({ name, initial }) {
const [isOn, toggle] = useToggle(initial)
return (
<div>
<p>{name}: {isOn ? "ON" : "OFF"}</p>
<button onClick={toggle}>Toggle</button>
</div>
)
}
function Spoiler() {
const [isOpen, toggle] = useToggle(false)
return (
<div>
<button onClick={toggle}>{isOpen ? "Tutup" : "Lihat jawaban"}</button>
{isOpen && <p>Jawabannya: custom hook!</p>}
</div>
)
}
useTogglecuma function biasa. Bedanya dengan function lain: namanya diawaliuse, dan dia memanggil hook (useState) di dalamnya.initial = falseadalah nilai bawaan parameter:useToggle()tanpa argumen mulai darifalse.setValue((v) => !v)membalik nilai terakhir: true jadi false, false jadi true. Jadi klik kedua selalu mengembalikan ke keadaan awal.return [value, toggle]mengembalikan sepasang isi, persis sepertiuseState. Yang memakai bebas menamainya:[isOn, toggle]di Lamp,[isOpen, toggle]di Spoiler.- Keuntungan utamanya: reuse logika stateful, yaitu logika yang memegang state dipakai ulang tanpa menyalin kode. Bug di
togglecukup dibetulkan di satu tempat.
Tiap panggilan punya state sendiri
App merender <Lamp name="Teras" initial={true} />, <Lamp name="Dapur" initial={true} />, dan <Spoiler />. Lalu kamu mengklik:
| Aksi | Teras | Dapur | Spoiler |
|---|---|---|---|
| Awal | ON | ON | tertutup |
| Klik Toggle di Teras | OFF | ON | tertutup |
| Klik Toggle di Teras lagi | ON | ON | tertutup |
| Klik Toggle di Dapur | ON | OFF | tertutup |
useToggle dipanggil tiga kali, jadi ada tiga useState yang terpisah. Dapur tetap ON waktu Teras jadi OFF. Itu jawaban tebakannya. Custom hook membagi cara kerjanya, bukan isi state-nya. Kalau dua komponen harus memegang nilai yang sama, angkat state-nya ke induk (lifting state) atau pakai Context.
Aturan custom hook
- Nama diawali
use:useToggle, bukantoggleatautoggleHelper. Dari awalan itu React dan linter-nya (eslint-plugin-react-hooks) tahu function ini hook, lalu mengecek aturannya. - Panggil hook di tingkat atas. Jangan di dalam
if, loop, atau function lain di dalamnya. Aturan ini berlaku juga di dalam custom hook. - Panggil hook cuma dari komponen atau custom hook lain, bukan dari function biasa.
- Bentuk kembaliannya bebas. Array seperti
[value, toggle]enak kalau pemakainya ingin menamai sendiri. Object juga boleh:
function useToggle() {
const [on, setOn] = useState(false)
const toggle = () => setOn((prev) => !prev)
return { on, toggle }
}
const { on, toggle } = useToggle() // dibaca dengan nama yang sama
🎯 Custom hook, function biasa, atau komponen?
- Butuh state atau effect (buka tutup, timer, localStorage): custom hook.
- Cuma menghitung atau memformat (total harga, format
Rp 15.000): function biasa, gak perluuse. - Bagian tampilan yang dipakai ulang (tombol dengan gaya yang sama): komponen.
- Logika yang dulu dibagi lewat HOC (
withSomething) sekarang hampir selalu ditulis sebagai custom hook: gak ada lapis komponen tambahan.
⚠️ Jebakan yang sering ketemu
- Mengira state-nya dibagi. Dua komponen yang memanggil
useTogglepunya state masing-masing. - Mengisi nilai tetap, bukan membalik.
setValue(false)cuma bisa mematikan; klik kedua gak mengembalikannya. Balik nilai sebelumnya dengansetValue((v) => !v). - Nilai awal yang salah.
useToggle(false)bikin bunyi pertamaOFF. Kalau harus mulaiON, panggiluseToggle(true). - Hook di dalam
if.if (aktif) { useToggle() }melanggar aturan hook: urutan hook harus sama di tiap render.
TL;DR: Custom hook adalah function berawalan use yang memanggil hook lain, misalnya useState, untuk reuse logika stateful tanpa menyalin kode. useToggle(initial = false) menyimpan satu boolean, membuat toggle yang membaliknya dengan setValue((v) => !v), dan mengembalikan [value, toggle]. Tiap pemanggilan punya state sendiri: Teras jadi OFF, Dapur tetap ON. Ikuti aturan hook: nama diawali use, dipanggil di tingkat atas, dan cuma dari komponen atau hook lain. Yang dikembalikan boleh array atau object.