Di React, kolom input biasanya dikendalikan state: isinya dibaca dari state, dan tiap ketikan ditulis balik ke state lewat onChange. Pola ini namanya controlled component. Bayangin layar pesanan di kasir fast food. Kamu menyebut pesanan, kasir mencatatnya di mesin, dan layar yang menghadap kamu cuma menampilkan isi catatan mesin itu. Kalau kasirnya gak mencatat, layarnya gak berubah, sekeras apa pun kamu menyebutnya. Di React, catatan mesinnya adalah state, layarnya adalah kolom input, dan kasirnya adalah onChange.
🧪 Tebakan cepat: ada kolom
<input value={username} />tanpaonChange, danusernamemasih"". Kamu mengetik "Budi". Kolomnya berisi Budi, atau tetap kosong? Tebak dulu, jawabannya ada di bagian "Controlled input: value dan onChange".
Controlled input: value dan onChange
function App() {
const [email, setEmail] = useState("")
return (
<div>
<input id="email" value={email} onChange={(e) => setEmail(e.target.value)} />
<p id="preview">{email}</p>
</div>
)
}
useState(""): nilai awalnya string kosong, karena kolomnya mulai kosong.value={email}: isi kolom dibaca dari state. Arahnya dari state ke layar.onChange={(e) => setEmail(e.target.value)}: tiap ketikan, React memanggilonChangedengan evente.e.targetadalah kolomnya, dane.target.valueisinya saat itu, sudah termasuk huruf baru. Arahnya dari ketikan ke state.<p id="preview">{email}</p>membaca state yang sama, jadi ikut berubah tiap ketikan.
Ini yang terjadi saat kamu mengetik rin:
| Ketikan | e.target.value |
Dipanggil | email di render berikutnya |
Kolom dan preview |
|---|---|---|---|---|
| r | "r" |
setEmail("r") |
"r" |
r |
| i | "ri" |
setEmail("ri") |
"ri" |
ri |
| n | "rin" |
setEmail("rin") |
"rin" |
rin |
Tanpa onChange, ketikanmu gak pernah sampai ke state. value tetap "", dan tiap render React menulis ulang kolomnya jadi kosong. Jadi kolomnya tetap kosong, seperti layar yang kasirnya gak mencatat. Itu jawaban tebakannya. React juga memberi peringatan di console bahwa kolom itu jadi read-only.
Form: onSubmit dan preventDefault
function App() {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const handleSubmit = (e) => {
e.preventDefault() // cegah browser memuat ulang halaman
console.log(username) // mencetak isi kolom saat itu, misalnya Budi
}
return (
<form onSubmit={handleSubmit}>
<input id="username" value={username} onChange={(e) => setUsername(e.target.value)} />
<input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
<button id="submit" type="submit">Login</button>
</form>
)
}
- Tiap kolom punya state sendiri,
usernamedanpassword, dan dua-duanya controlled. onSubmitdipasang di<form>, bukan di tombol. Form terkirim lewat klik tomboltype="submit"maupun tombol Enter di kolomnya, dan dua-duanya memicuonSubmit. Tombolnya harus ada di dalam<form>.- Bawaan browser saat form terkirim adalah memuat ulang halaman. Semua state hilang, dan output console ikut bersih.
e.preventDefault()membatalkan bawaan itu. console.log(username)membaca state, jadi yang tercetak adalah apa yang sedang ada di kolom.
Ketik Budi, lalu tekan Login: console mencetak Budi, dan halamannya tetap di tempat.
Controlled atau uncontrolled
Kolom yang gak diberi value disebut uncontrolled: browser yang menyimpan isinya, bukan state.
| Controlled | Uncontrolled | |
|---|---|---|
| Isi kolom disimpan di | state React | browser |
| Ditulis | value={x} dan onChange |
tanpa value (boleh defaultValue) |
| Bisa dikosongkan dari kode | ya, setX("") |
gak langsung |
| Isinya bisa dipakai saat diketik | ya, langsung dari state | cuma lewat event |
Pakai controlled kalau isi kolom ikut dipakai selagi diketik (preview, cek panjang password, tombol yang baru aktif kalau isian lengkap) atau perlu dikosongkan sesudah dikirim. Untuk latihan di pelajaran ini, semua kolom controlled.
⚠️ Jebakan yang sering ketemu
valuetanpaonChange. Kolomnya gak bisa diketik. Pasang dua-duanya.- Lupa
e.preventDefault(). Halaman dimuat ulang begitu form terkirim, jadi state danconsole.log-nya hilang. Baris pertamahandleSubmithampir selalue.preventDefault(). - Nilai awal
undefined.useState()laluvalue={nama}membuat kolomnya mulai sebagai uncontrolled, dan React memberi peringatan waktu nilainya jadi string. Mulai dariuseState(""). - Checkbox dan angka. Checkbox dibaca dengan
checked={setuju}dane.target.checked, bukanvalue. Daritype="number",e.target.valuetetap string:"5" + 1jadi"51". Ubah dulu denganNumber(e.target.value).
TL;DR: Controlled component adalah input yang isinya dikendalikan state: value={email} untuk arah state ke layar, onChange={(e) => setEmail(e.target.value)} untuk arah ketikan ke state. Nilai awalnya useState(""). Form ditangani dengan onSubmit di <form>, tombolnya type="submit" di dalam form, dan handleSubmit dimulai dengan e.preventDefault() supaya halaman gak dimuat ulang. Isi state bisa langsung dipakai, misalnya console.log(username) atau preview <p>{email}</p>. Checkbox pakai checked, angka dari input tetap string.