State adalah data yang diingat komponen di antara render, misalnya angka di counter atau status aktif sebuah tombol. Kamu menyimpannya dengan hook useState (hook adalah fungsi khusus React yang namanya diawali use). Begitu state diganti lewat setter-nya, React render ulang komponen itu: memanggil function komponennya lagi dan memperbarui halaman sesuai hasilnya. Bayangin papan skor futsal. Angkanya dipegang operator, bukan pemain. Pemain cuma bisa bilang "skornya jadi 1", lalu operator memasang angka baru dan semua orang membaca papan yang baru. Di React, operatornya React, dan papan yang sedang terpasang adalah render yang sedang berjalan.
🧪 Tebakan cepat:
countmulai dari 0. Satu klik menjalankansetCount(count + 1)tiga kali berturut-turut. Angkanya jadi 1 atau 3? Tebak dulu, jawabannya ada di bagian "Tiap render memegang nilainya sendiri".
Kenapa variabel biasa gak cukup
function Counter() {
let count = 0
return (
<button onClick={() => { count = count + 1 }}>
Diklik {count} kali
</button>
)
}
Diklik berapa kali pun, tombolnya tetap "Diklik 0 kali". Ada dua alasan:
- Mengubah variabel biasa gak memberi tahu React apa-apa, jadi gak ada render ulang.
- Seandainya ada render ulang pun, function
Counterjalan lagi dari atas, danlet count = 0membuatnya nol lagi.
State menyelesaikan dua-duanya: nilainya disimpan React di luar function, dan setter-nya meminta render ulang.
useState: nilai dan setter
import { useState } from "react"
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<span id="count">{count}</span>
<button id="increment" onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
import { useState } from "react"mengambil hook-nya dari React. Kode awal di editor latihan juga mulai dengan baris ini, persis seperti di proyek sungguhan.useState(0):0adalah nilai awal, cuma dipakai di render pertama.useStatemengembalikan array berisi dua hal, danconst [count, setCount]memberi nama keduanya:countnilai saat ini,setCountfungsi untuk menggantinya. Namanya bebas, tapi kebiasaannya[sesuatu, setSesuatu].setCount(count + 1)meminta React: "count berikutnya 1 lebih besar". React render ulangCounter, dan kali iniuseStatemengembalikan nilai baru.
| Kejadian | count di render ini |
Yang diminta | Tampil |
|---|---|---|---|
| Render pertama | 0 | 0 | |
| Klik +1 | 0 | setCount(0 + 1) |
|
| Render ke-2 | 1 | 1 | |
| Klik +1 | 1 | setCount(1 + 1) |
|
| Render ke-3 | 2 | 2 |
Saklar: state true atau false
State gak harus angka. Untuk saklar, simpan boolean dan balik nilainya:
function App() {
const [active, setActive] = useState(true)
return (
<div>
<p id="status">{active ? "Aktif" : "Nonaktif"}</p>
<button id="toggle" onClick={() => setActive(!active)}>Toggle</button>
</div>
)
}
useState(true)membuat status awalnya aktif, jadi paragrafnya mulai dariAktif.active ? "Aktif" : "Nonaktif"memilih satu dari dua kata sesuai state.setActive(!active)menyimpan kebalikannya. Klik sekali jadiNonaktif, klik lagi kembaliAktif.
Tiap render memegang nilainya sendiri
Di dalam satu render, count itu konstanta. setCount gak mengubah count yang sedang dipegang handler, dia menjadwalkan render baru dengan nilai baru.
function handleClick() {
setCount(count + 1) // count di render ini 0: minta "jadikan 1"
setCount(count + 1) // count masih 0: minta "jadikan 1" lagi
setCount(count + 1) // sama: "jadikan 1"
console.log(count) // 0, nilai milik render ini
}
Satu klik dari 0 hasilnya 1, bukan 3. Itu jawaban tebakannya. Sama kayak bilang ke operator "jadikan 1" tiga kali: papannya tetap 1. console.log(count) mencetak 0, karena handler ini milik render yang count-nya 0. Nilai 1 baru ada di render berikutnya.
Kalau mau "tambah 1" tiga kali, kirim fungsi, bukan nilai:
function handleClick() {
setCount(c => c + 1) // antrean: 0 + 1 = 1
setCount(c => c + 1) // antrean: 1 + 1 = 2
setCount(c => c + 1) // antrean: 2 + 1 = 3
}
c => c + 1disebut updater function. React menjalankannya berurutan, dan tiap updater menerima hasil updater sebelumnya.- Hasilnya 3, dengan satu render saja. React mengumpulkan semua permintaan dari satu event dulu (namanya batching), lalu render sekali.
- Patokannya: nilai baru dihitung dari nilai lama (tambah, kurang, balik true/false)? Pakai updater:
setCount(c => c + 1),setActive(a => !a). Nilai barunya gak bergantung nilai lama, misalnyasetCount(0)? Kirim nilainya langsung.
Object dan array: bikin yang baru
React membandingkan state lama dan baru. Kalau yang dikirim object yang sama persis, React menganggapnya gak berubah dan layar gak diperbarui.
const [user, setUser] = useState({ name: "Budi", age: 17 })
user.age = 18 // salah: mengubah object lama
setUser(user) // object yang sama, layar gak berubah
setUser({ ...user, age: 18 }) // benar: object baru, isinya disalin lalu age diganti
setItems([...items, "baru"]) // array baru dengan satu isi tambahan
⚠️ Jebakan yang sering ketemu
count++ataucount = 5.countituconst, dan mengubahnya gak memicu render. Selalu lewat setter, misalnyasetCount(...)atausetActive(...).- Lupa nilai awal.
useState()tanpa angka membuatcountjadiundefined, dan{undefined}gak menampilkan apa-apa. Counter yang harus mulai dari 0 ditulisuseState(0). - useState di dalam
ifatau loop. Hook harus dipanggil di level paling luar komponen, bukan di dalamif, loop, atau fungsi lain, supaya urutannya sama tiap render. React mengenali tiap state dari urutan panggilannya. - Memanggil setter langsung di badan komponen.
setCount(0)di luar handler memicu render, yang memanggilsetCountlagi, terus-menerus. React berhenti dengan error "Too many re-renders".
TL;DR: const [count, setCount] = useState(0) menyimpan state dengan nilai awal 0. count adalah nilai di render ini, dan gak berubah selama render itu. setCount(...) menjadwalkan render baru dengan nilai baru, jadi console.log(count) sesudahnya masih mencetak nilai lama. setCount(count + 1) tiga kali dalam satu klik cuma menambah 1, setCount(c => c + 1) tiga kali menambah 3. Untuk saklar, simpan boolean dan balik dengan setActive(!active). Object dan array diganti dengan yang baru ({ ...user, age: 18 }), jangan diubah langsung. Hook selalu dipanggil di level paling luar komponen, bukan di dalam if atau loop.