useState Adalah: Cara Kerja State dan Hook useState di React untuk Pemula
useState adalah hook React untuk menyimpan data yang bisa berubah di dalam komponen. Pahami sintaks, perbedaan state vs variabel biasa, functional update, dan aturan pentingnya lewat contoh interaktif.
Pembuat Hyper Sheets & BelajarWeb.dev
useState adalah hook bawaan React yang dipakai untuk menyimpan data yang bisa berubah di dalam sebuah komponen, dan yang paling penting: setiap kali data itu berubah lewat fungsi setter-nya, React otomatis merender ulang komponen sehingga tampilan di layar ikut ter-update. Inilah jantung dari cara React membuat UI menjadi interaktif. Isi keranjang belanja, angka pada tombol like, teks yang kamu ketik di form, tab yang sedang aktif, semua “data yang berubah seiring waktu” itu disimpan di dalam state, dan useState adalah cara paling dasar untuk membuatnya.
useState adalah hook pertama yang wajib dikuasai siapa pun yang belajar React, karena hampir tidak ada komponen nyata yang tidak memakainya. Kabar baiknya, konsepnya sebenarnya sederhana begitu kamu paham satu ide inti: di React, kamu tidak mengubah tampilan secara langsung, kamu mengubah state, lalu React yang mengurus tampilannya. Di panduan ini kamu akan memahami useState benar-benar dari nol: apa itu state, sintaksnya, kenapa variabel biasa tidak cukup, cara update yang benar (termasuk functional update), sampai kesalahan umum yang bikin pemula frustrasi, lengkap dengan playground yang bisa kamu klik-klik sendiri.
Apa Itu State di React?
State adalah data internal milik sebuah komponen yang bisa berubah seiring waktu, dan yang membuat komponen “mengingat” sesuatu di antara interaksi. Bayangkan sebuah tombol like: berapa jumlah like sekarang? Itu state. Sebuah form login: apa yang sedang diketik pengguna di kolom email? Itu juga state. Setiap kali nilai ini berubah, tampilan harus ikut menyesuaikan, dan di sinilah useState berperan.
Sebelum ada hooks, hanya komponen berbasis class yang bisa punya state. Sejak React 16.8, useState memungkinkan komponen fungsi (function component), yang jauh lebih ringkas, untuk menyimpan state juga. Sekarang, function component + hooks adalah cara standar menulis React modern.
Sintaks Dasar useState
useState dipanggil dengan sebuah nilai awal, lalu mengembalikan array berisi dua elemen: nilai state saat ini, dan fungsi untuk mengubahnya. Kita “membongkar” array itu dengan array destructuring:
import { useState } from 'react'
function Counter() {
const [hitung, setHitung] = useState(0)
// ↑ nilai ↑ fungsi ↑ nilai awal
// saat ini pengubah
return (
<button onClick={() => setHitung(hitung + 1)}>
Sudah diklik {hitung} kali
</button>
)
}
Mari bedah barisnya:
useState(0), memberi tahu React: “buatkan sebuah state dengan nilai awal0”.hitung, variabel yang berisi nilai state saat ini. Di render pertama nilainya0.setHitung, fungsi untuk mengubah state. Memanggilnya akan menyimpan nilai baru dan menyuruh React merender ulang komponen.
Konvensi penamaannya konsisten: kalau state-nya bernama hitung, setter-nya setHitung; kalau nama, setter-nya setNama. Selalu diawali set diikuti nama state dengan huruf besar di depan.
Nilai awal bisa tipe apa saja, angka, string, boolean, array, bahkan object:
const [nama, setNama] = useState('') // string kosong
const [aktif, setAktif] = useState(false) // boolean
const [items, setItems] = useState([]) // array kosong
const [user, setUser] = useState({ nama: '', umur: 0 }) // object
Kenapa Tidak Pakai Variabel Biasa Saja?
Ini pertanyaan paling penting, dan jawabannya adalah alasan useState benar-benar ada. Kalau kamu memakai variabel biasa (let), React tidak akan pernah tahu bahwa nilainya berubah, sehingga tampilan tidak akan pernah ter-update. Bandingkan dua kode ini:
// ❌ SALAH, pakai variabel biasa
function CounterSalah() {
let hitung = 0
return (
<button onClick={() => {
hitung = hitung + 1 // nilainya memang berubah...
console.log(hitung) // ...dan console mencetak 1, 2, 3
}}>
Diklik {hitung} kali // ...TAPI di layar tetap 0!
</button>
)
}
Di kode di atas, hitung memang bertambah di memori, tapi angka di layar tidak pernah berubah. Kenapa? Karena dua hal: (1) React tidak tahu ia harus merender ulang, dan (2) bahkan kalau dirender ulang, let hitung = 0 akan mereset nilainya ke 0 lagi. Variabel biasa tidak “bertahan” antar render.
// ✅ BENAR, pakai useState
function CounterBenar() {
const [hitung, setHitung] = useState(0)
return (
<button onClick={() => setHitung(hitung + 1)}>
Diklik {hitung} kali // angka di layar ikut berubah
</button>
)
}
useState menyelesaikan kedua masalah itu sekaligus: React mengingat nilai state antar render, dan memanggil setHitung memicu render ulang supaya layar sinkron dengan data terbaru.
Coba Sendiri: State dalam Aksi
Teori paling cepat klik saat kamu melihatnya bergerak. Di playground berikut ada dua state: hitung (angka) dan nama (teks). Klik tombol + / −, ketik namamu di kolom input, lalu perhatikan tiga hal: angka yang berubah, sapaan yang muncul otomatis, dan indikator “Dirender” di pojok kanan atas yang naik setiap kali kamu mengubah state.
Coba Sendiri
Dirender: 1×State angka: hitung
State teks: nama
Sapaan akan muncul di sini saat kamu mengetik…
Isi state saat ini
hitung = 0 nama = ""
Perhatikan angka Dirender di pojok kanan atas: setiap kali kamu memanggil setHitung atau setNama, React merender ulang komponen ini dan tampilan ikut ter-update.
Perhatikan pola yang muncul saat kamu bermain:
- Setiap kali kamu memanggil
setHitungatausetNama(lewat tombol atau ketikan), angka Dirender bertambah, itu bukti bahwa mengubah state memicu render ulang. - Tampilan (angka besar dan sapaan) selalu mencerminkan isi state terbaru, tanpa kamu menyentuh DOM sama sekali.
- Kedua state hidup berdampingan dan independen: mengubah
namatidak menyentuhhitung, dan sebaliknya.
Functional Update: Cara Update yang Aman
Kalau nilai baru state bergantung pada nilai sebelumnya, gunakan bentuk fungsi (functional update): setHitung((prev) => prev + 1) alih-alih setHitung(hitung + 1). Ini terlihat sepele tapi menyelamatkanmu dari bug halus. Perhatikan masalah ini:
// Niatnya menambah 3, tapi hasilnya cuma +1!
function tambahTiga() {
setHitung(hitung + 1)
setHitung(hitung + 1)
setHitung(hitung + 1)
}
Kalau hitung awalnya 0, kamu mungkin mengira hasilnya 3. Tapi hasilnya 1. Kenapa? Karena hitung di dalam fungsi ini “membeku” pada nilai render saat ini (0), jadi ketiga baris itu sama-sama menghitung 0 + 1. Solusinya adalah functional update:
// ✅ Sekarang benar-benar bertambah 3
function tambahTiga() {
setHitung((prev) => prev + 1)
setHitung((prev) => prev + 1)
setHitung((prev) => prev + 1)
}
Dengan bentuk (prev) => prev + 1, React memberikan nilai terbaru yang tertunggak ke setiap panggilan, sehingga ketiganya berantai dengan benar. Aturan praktisnya: kalau update baru butuh nilai lama, selalu pakai bentuk fungsi.
Jangan Mutasi State Secara Langsung
State di React harus diperlakukan sebagai read-only, jangan pernah mengubah array atau object state secara langsung; selalu buat salinan baru. Ini jebakan besar saat state-mu berupa array atau object.
const [items, setItems] = useState(['apel', 'mangga'])
// ❌ SALAH, memutasi array lama secara langsung
function tambahSalah() {
items.push('jeruk') // mengubah array yang sama
setItems(items) // React tidak melihat perubahan → tidak render ulang
}
// ✅ BENAR, buat array BARU dengan spread operator
function tambahBenar() {
setItems([...items, 'jeruk']) // array baru → React mendeteksi perubahan
}
React mendeteksi perubahan dengan membandingkan referensi object/array, bukan isinya. Kalau kamu memutasi array yang sama (push), referensinya tidak berubah, jadi React mengira “tidak ada yang berubah” dan tidak merender ulang. Membuat salinan baru dengan spread operator ([...items]) atau {...user} memberi React object baru yang jelas berbeda.
const [user, setUser] = useState({ nama: 'Budi', umur: 22 })
// ✅ update satu properti object dengan menyalin sisanya
setUser({ ...user, umur: 23 })
State vs Props: Jangan Sampai Tertukar
Dua konsep ini sering membingungkan pemula. Singkatnya: state adalah data yang dimiliki dan dikelola sendiri oleh komponen (bisa diubah dari dalam), sedangkan props adalah data yang dikirim dari komponen induk (bersifat read-only bagi si penerima). State untuk hal yang berubah karena interaksi di dalam komponen itu; props untuk mengoper data ke bawah, dari parent ke child.
Sebuah komponen bisa memakai keduanya sekaligus: menerima props dari induk, lalu menyimpan state internalnya sendiri. Untuk latihan interaktif menulis useState sungguhan dan melihat state ter-update langsung di browser, lanjutkan ke materi useState di learnhypersheets, kamu akan menulis komponen ber-state sendiri dan langsung melihat hasilnya.
Kesalahan Umum Pemula dengan useState
Sebelum lanjut, kenali jebakan yang paling sering menimpa pemula:
- Memanggil
useStatedi dalam if, loop, atau fungsi bersarang. Hook harus dipanggil di level teratas komponen, dalam urutan yang sama di setiap render. Jangan pernahif (kondisi) { useState(...) }. - Memutasi state langsung dengan
push,splice, atauuser.umur = 23, lalu bingung kenapa layar tidak update. Selalu buat salinan baru. - Mengira state langsung berubah setelah memanggil setter di baris yang sama. Update bersifat asinkron.
- Menaruh terlalu banyak hal di satu state object padahal lebih rapi dipecah jadi beberapa
useStateterpisah.
Di dalam function component, kamu menulis "let hitung = 0" lalu menaikkannya dengan hitung++ saat tombol diklik. Kenapa angka di layar tidak ikut berubah?
FAQ Seputar useState
Apa itu useState dengan bahasa sederhana?
useState adalah cara React “mengingat” sebuah nilai yang bisa berubah di dalam komponen, misalnya jumlah klik, teks yang diketik, atau status buka/tutup. Kamu memberinya nilai awal, dan ia memberimu dua hal: nilai saat ini dan sebuah tombol ajaib (fungsi setter) untuk mengubahnya. Setiap kali kamu menekan tombol ajaib itu, React otomatis memperbarui tampilan agar cocok dengan nilai terbaru.
Apa bedanya useState dan variabel biasa dengan let?
Variabel biasa tidak memicu React merender ulang, dan nilainya hilang (reset) setiap kali komponen dirender. useState menyelesaikan keduanya: nilainya bertahan antar render, dan mengubahnya lewat setter membuat React menggambar ulang tampilan. Kalau sebuah nilai memengaruhi apa yang tampil di layar dan bisa berubah, ia hampir selalu harus jadi state.
Kenapa nilai state tidak langsung berubah setelah setState?
Karena update state di React bersifat asinkron, React menjadwalkan perubahan, lalu menerapkannya pada render berikutnya, bukan seketika di baris setelahnya. Jadi kalau kamu console.log state tepat setelah memanggil setter, kamu masih melihat nilai lama. Kalau perhitungan berikutnya butuh nilai terbaru, gunakan bentuk functional update set(prev => ...).
Kapan harus pakai functional update set(prev => …)?
Setiap kali nilai baru dihitung dari nilai sebelumnya: menaikkan counter, menoggle boolean, atau menambah/menghapus item dari array. Bentuk ini memastikan kamu selalu bekerja dengan nilai terbaru, bahkan ketika beberapa update terjadi berdekatan. Kalau kamu hanya menimpa dengan nilai baru yang tidak bergantung nilai lama, bentuk biasa setNama('Budi') sudah cukup.
Bolehkah pakai lebih dari satu useState dalam satu komponen?
Boleh, dan justru dianjurkan. Kamu bisa punya useState terpisah untuk setiap potongan data, satu untuk nama, satu untuk email, satu untuk status loading. Memecah state jadi beberapa variabel kecil yang jelas biasanya lebih mudah dibaca dan di-debug daripada satu object besar. Pastikan setiap useState dipanggil di level teratas komponen, tidak di dalam if atau loop.
Apa hubungan useState dengan useEffect?
Keduanya hook paling fundamental di React dan sering dipakai bersama. useState menyimpan data yang berubah, sedangkan useEffect menjalankan side effect (seperti fetch data) sebagai reaksi terhadap perubahan. Contoh umum: useState menyimpan hasil dari server, dan useEffect yang memicu pengambilan datanya. Kuasai useState dulu, karena ia fondasi yang dipakai hampir semua hook lain.
Sampai di sini, kamu sudah memahami inti useState: ia menyimpan data yang berubah, dan memanggil setter-nya membuat React merender ulang tampilan agar sinkron dengan state terbaru. Ingat tiga kebiasaan penting, pakai setter (bukan variabel biasa), gunakan functional update saat nilai baru bergantung nilai lama, dan jangan pernah memutasi state secara langsung, maka mayoritas bug state tidak akan pernah menyentuhmu. Buka lagi playground di atas kapan pun kamu ragu, dan teruslah membangun!