State dengan useState — React

Bayangin tombol Like di Instagram. Di-klik — angka likenya naik 1, ikon hati berubah merah, animasi heartbeat jalan. Klik lagi — semua kebalik. Komponen yang…

Bayangin tombol Like di Instagram. Di-klik — angka likenya naik 1, ikon hati berubah merah, animasi heartbeat jalan. Klik lagi — semua kebalik. Komponen yang sama, tapi tampilannya berubah-ubah seiring waktu. Itu state: data yang bisa berubah di dalam komponen, dan tiap kali berubah → React otomatis re-render UI biar sinkron sama data. Tanpa state, komponen kamu cuma "foto statis" yang gak bisa respon ke aksi user. Dengan state, komponen jadi "hidup" — interaktif, reaktif, dinamis.

💡 useState = hook paling fundamental. Hampir tiap komponen interaktif di React mulai dari useState. Form input, tombol toggle, modal, counter, theme switcher — semua dasarnya useState. Kuasai hook ini = unlock 80% kemampuan React. Hook lain (useEffect, useReducer, dll) dibangun di atas konsep yang sama.

import { useState } from "react"

function Counter() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>Hitungan: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  )
}

Cara Kerja useState

useState(initialValue) return ARRAY dengan 2 elemen — destructure jadi pair:

const [count, setCount] = useState(0)
//     ^value   ^setter         ^initial

Updater Function — Lebih Aman

// CARA BIASA — bisa stale kalau dipanggil cepat berturut-turut
setCount(count + 1)

// UPDATER FUNCTION — pakai ini kalau update berdasarkan state sebelumnya
setCount(prev => prev + 1)

Bedanya keliatan pas setCount dipanggil 3x dalam 1 event handler:

const handleClick = () => {
  setCount(count + 1)  // count = 0, set ke 1
  setCount(count + 1)  // count masih 0 (belum re-render!), set ke 1 (sama!)
  setCount(count + 1)  // sama. Hasil: count = 1, BUKAN 3
}

// Dengan updater:
const handleClick = () => {
  setCount(prev => prev + 1)  // 0 → 1
  setCount(prev => prev + 1)  // 1 → 2
  setCount(prev => prev + 1)  // 2 → 3 ✓
}

State untuk Object/Array — Wajib Buat Baru

// SALAH — mutate langsung, React gak detect change
const [user, setUser] = useState({ name: "Budi", age: 17 })
user.age = 18           // ❌ mutate
setUser(user)           // React lihat object SAMA, gak re-render

// BENAR — buat object baru dengan spread
setUser({ ...user, age: 18 })   // ✓ object baru, React re-render

// Array juga sama:
setItems([...items, newItem])         // tambah
setItems(items.filter(x => x !== y))  // hapus
setItems(items.map(x => x.id === id ? {...x, done: true} : x))  // update

🎭 Analogi sehari-hari

useState itu kayak papan tulis kelas. Pas guru mau ganti tulisan, dia gak nyamberin papan langsung pake spidol baru — dia hapus dulu yang lama, terus tulis yang baru (setCount(prev => prev + 1)). Papan tulis sama, tulisan beda. Tiap kali tulisan ganti, semua murid yang lagi liat papan otomatis ngeliat yang baru — itu re-render. Kalau guru cuma bikin coretan kecil di tulisan lama (mutate object), murid-murid yang udah nyatet "papan udah keisi nih" gak akan ngeh ada yang berubah. Makanya: SELALU bikin "tulisan baru" (object/array baru), JANGAN coret-coret yang lama.

⚠️ Jebakan yang sering ditemui

🎯 Pilih bentuk state — gimana caranya?

  • Nilai independen sederhana (counter, toggle) → useState(0), useState(false)
  • String inputuseState("")
  • Object terkait (form fields, settings) → useState({ name: "", email: "" }) lalu setForm({ ...form, name })
  • List itemsuseState([]) lalu spread/filter/map untuk update
  • State KOMPLEKS dengan banyak action (cart dengan add/remove/update/clear) → useReducer (lesson nanti)
  • State yang BERSAMA banyak komponen → lifting state up atau Context API

Aturan praktis: mulai dengan useState simpel. Naik ke useReducer kalau handler-nya udah membesar (5+ jenis update).

🧪 Tebakan cepat — output apa? Tanpa run, tebak hasil setelah klik tombol:

const [count, setCount] = useState(0)
const handleClick = () => {
  setCount(count + 1)
  setCount(count + 1)
  setCount(count + 1)
}
// Klik tombol sekali — count jadi berapa?

Jawaban: 1, bukan 3. Karena count masih 0 saat 3 panggilan, semua kompute 0 + 1. Fix: pakai updater setCount(prev => prev + 1) → hasil 3. Bug paling klasik di useState.

TL;DR: useState(initial) return [value, setter]. Setter dipanggil → komponen re-render dengan nilai baru. WAJIB bikin object/array baru untuk update (jangan mutate — setUser({...user, age: 18})). Pakai updater setX(prev => prev + 1) kalau update berdasarkan state lama atau panggil setter berturut-turut. State update gak sync — setCount(5); console.log(count) masih lama. Hook hanya di top-level component (bukan di if/loop). State kompleks → useReducer.

Yang akan kamu pelajari