ReactJS

Belum masuk. Progresmu tidak tersimpan.

Masuk
  • Bab 5 dari 10
  • 30 menit
  • 1 latihan kode
  • 4 soal kuis

Event dan useState: membuat komponen bereaksi

Di bab ini (10 bagian)
  1. Tujuan
  2. Event di React: mirip HTML, beda sedikit
  3. Oper fungsinya, jangan panggil
  4. State dengan useState
  5. Kenapa set state memicu render ulang
  6. Update berdasar nilai sebelumnya
  7. Batching: banyak set, satu render
  8. State object dan array: immutable
  9. Kesalahan umum
  10. Rangkuman

Tujuan bab ini

  • Menangani event pengguna dengan onClick, onChange, dan onSubmit, serta paham kenapa yang dioper adalah fungsi, bukan hasil pemanggilannya.
  • Menyimpan data yang berubah dengan useState dan memperbaruinya lewat fungsi set.
  • Memperbarui state berdasarkan nilai sebelumnya dengan setX(prev => ...), termasuk state berupa object dan array secara immutable.
  • Memahami kenapa perubahan state memicu render ulang dan apa itu batching.

Event di React: mirip HTML, beda sedikit

Di HTML biasa kamu mungkin pernah menulis <button onclick="simpan()">. Di React, aturannya sedikit berbeda:

  • Nama event ditulis camelCase: onClick, onChange, onSubmit, onKeyDown, onMouseEnter.
  • Nilainya adalah fungsi di dalam kurung kurawal, bukan string.
function FormPencarian() {
  function handleSubmit(e) {
    e.preventDefault(); // cegah browser me-reload halaman
    console.log("form dikirim");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input onChange={(e) => console.log(e.target.value)} />
      <button onClick={() => console.log("klik")}>Cari</button>
    </form>
  );
}

Parameter e adalah objek event. Dua properti yang paling sering kamu pakai: e.target.value untuk membaca isi input, dan e.preventDefault() untuk mencegah perilaku bawaan browser, misalnya form yang otomatis mengirim dan me-reload halaman.

EventKapan terpicuContoh pemakaian
onClickElemen diklikTombol tambah, hapus, buka menu
onChangeIsi input, textarea, atau select berubahKolom pencarian, form daftar
onSubmitForm dikirim (klik tombol submit atau Enter)Login, kirim komentar
onKeyDownTombol keyboard ditekanShortcut, tutup modal dengan Escape
onMouseEnterKursor masuk ke area elemenTooltip, efek hover

Catatan kecil: di React, onChange pada input terpicu setiap ketikan, bukan hanya saat input kehilangan fokus seperti event change di HTML biasa.

Oper fungsinya, jangan panggil

Ini kesalahan nomor satu pemula. Bandingkan:

<button onClick={sapa}>Sapa</button>        // benar: oper fungsinya
<button onClick={sapa()}>Sapa</button>      // salah: sapa() langsung jalan saat render
<button onClick={() => sapa("Ayu")}>Sapa</button> // benar: butuh argumen? bungkus arrow function

Analoginya begini: onClick={sapa} itu seperti memberi nomor HP temanmu ke satpam, "telepon dia kalau ada tamu". Sedangkan onClick={sapa()} itu kamu langsung menelepon temanmu saat itu juga, lalu memberikan hasil teleponnya ke satpam. React mengevaluasi semua yang ada di dalam {} saat render, jadi sapa() jalan setiap kali komponen dirender, bukan saat diklik. Kalau di dalam sapa ada fungsi set state, hasilnya bisa render tanpa henti dan React menampilkan error "Too many re-renders".

State dengan useState

Variabel biasa di dalam komponen tidak cukup untuk data yang berubah. Coba ini:

function PenghitungRusak() {
  let angka = 0;
  return <button onClick={() => { angka++; }}>Klik: {angka}</button>;
}
// Tampilan tetap "Klik: 0" walau diklik berkali-kali

Ada dua masalah: React tidak tahu bahwa angka berubah, jadi tidak merender ulang; dan setiap kali fungsi komponen dipanggil lagi, let angka = 0 mulai dari nol lagi. Solusinya adalah state, yaitu data milik komponen yang diingat React di antara render.

import { useState } from "react";

function Penghitung() {
  const [angka, setAngka] = useState(0);

  return (
    <div>
      <h2>Jumlah klik: {angka}</h2>
      <button onClick={() => setAngka(angka + 1)}>Tambah</button>
      <button onClick={() => setAngka(0)}>Reset</button>
    </div>
  );
}

Langkah-langkahnya:

  1. Deklarasi: useState(0) membuat state dengan nilai awal 0. Hasilnya array berisi dua hal, yang kita ambil dengan destructuring: nilai saat ini (angka) dan fungsi pengubah (setAngka).
  2. Pemakaian: tampilkan angka di JSX seperti variabel biasa.
  3. Pembaruan: panggil setAngka(nilaiBaru). Jangan pernah angka++ atau angka = 5, karena React tidak akan tahu ada perubahan.

Bedakan dengan props: props datang dari komponen induk dan bersifat read-only, sedangkan state milik komponen itu sendiri dan boleh diubah lewat fungsi set.

Kenapa set state memicu render ulang

Saat kamu memanggil setAngka(1), React menyimpan nilai baru lalu memanggil ulang fungsi komponenmu. Kali ini useState mengembalikan 1, JSX yang dihasilkan berisi "Jumlah klik: 1", dan React hanya memperbarui bagian DOM yang berbeda. Jadi tampilan selalu hasil dari data: UI = f(state).

Konsekuensi penting: di dalam satu render, nilai angka itu seperti foto. Memanggil setAngka tidak mengubah variabel angka yang sedang kamu pegang, nilai baru baru terlihat di render berikutnya.

function handleKlik() {
  setAngka(angka + 1);
  console.log(angka); // masih nilai lama, misalnya 0
}

React juga membandingkan nilai baru dengan yang lama memakai Object.is. Kalau sama persis, React boleh melewati render ulang.

Update berdasar nilai sebelumnya

Bagaimana kalau kamu memanggil set tiga kali berturut-turut?

function tambahTiga() {
  setAngka(angka + 1); // 0 + 1
  setAngka(angka + 1); // 0 + 1
  setAngka(angka + 1); // 0 + 1
}
// hasilnya 1, bukan 3

Ketiganya membaca foto yang sama, yaitu angka = 0. Solusinya adalah updater function: oper fungsi yang menerima nilai sebelumnya.

function tambahTiga() {
  setAngka((prev) => prev + 1); // 0 -> 1
  setAngka((prev) => prev + 1); // 1 -> 2
  setAngka((prev) => prev + 1); // 2 -> 3
}
// hasilnya 3

React menaruh fungsi-fungsi ini di antrean lalu menjalankannya berurutan, masing-masing menerima hasil dari yang sebelumnya. Kebiasaan yang bagus: kalau nilai baru bergantung pada nilai lama, pakai bentuk prev => ....

Batching: banyak set, satu render

Pada contoh di atas, apakah komponen dirender tiga kali? Tidak. React mengumpulkan semua pembaruan state dalam satu event handler, lalu merender sekali setelah handler selesai. Ini disebut batching. Analoginya pelayan restoran yang mencatat semua pesananmu dulu, baru ke dapur sekali jalan, bukan bolak-balik untuk setiap menu. Sejak React 18, batching otomatis berlaku juga di dalam setTimeout, promise, dan event lain, tidak hanya di event handler React.

State object dan array: immutable

State boleh berupa object atau array, tapi kamu harus memperlakukannya sebagai read-only. Jangan ubah isinya langsung, buat salinan baru.

const [profil, setProfil] = useState({ nama: "Ayu", kota: "Bandung" });

// SALAH: mengubah object yang sama, referensinya tidak berubah
profil.kota = "Depok";
setProfil(profil); // Object.is(lama, baru) true, React bisa mengabaikan

// BENAR: object baru dengan spread
setProfil((prev) => ({ ...prev, kota: "Depok" }));

Untuk array, pakai method yang menghasilkan array baru:

TujuanHindari (mutasi)Pakai (immutable)
Menambahpush, unshift[...arr, item], [item, ...arr]
Menghapussplicearr.filter((x) => x.id !== id)
Mengubah satu itemarr[i] = ...arr.map((x) => x.id === id ? { ...x, selesai: true } : x)
Mengurutkansort langsung[...arr].sort(...) atau toSorted(...)
const [tugas, setTugas] = useState([]);

function tambah(judul) {
  setTugas((prev) => [...prev, { id: Date.now(), judul, selesai: false }]);
}

function toggle(id) {
  setTugas((prev) =>
    prev.map((t) => (t.id === id ? { ...t, selesai: !t.selesai } : t))
  );
}

Kenapa harus begini? Karena React mendeteksi perubahan dengan membandingkan referensi. Object baru berarti referensi baru, dan React tahu harus merender ulang.

Kesalahan umum

  • onClick={fungsi()}: fungsi jalan saat render. Tulis onClick={fungsi} atau onClick={() => fungsi(arg)}.
  • Mengubah state langsung (angka++, arr.push(x), obj.a = 1): tampilan tidak ikut berubah. Selalu lewat fungsi set dengan nilai baru.
  • Membaca state tepat setelah set dan berharap nilainya sudah baru. Nilai baru baru muncul di render berikutnya.
  • Lupa e.preventDefault() di onSubmit: halaman ter-reload dan state hilang.
  • Spread yang dangkal: { ...prev } hanya menyalin satu level. Object di dalam object juga perlu di-spread.

Rangkuman

  • Event React ditulis camelCase dan menerima fungsi. Oper referensinya atau bungkus dengan arrow function, jangan panggil langsung.
  • useState(awal) mengembalikan [nilai, setNilai]. Memanggil set menyimpan nilai baru dan membuat React merender ulang komponen.
  • Di dalam satu render, state seperti foto. Pakai setX(prev => ...) bila nilai baru bergantung pada nilai lama.
  • React melakukan batching: banyak set dalam satu event menghasilkan satu render.
  • Object dan array di state diperbarui secara immutable dengan spread, map, dan filter.

Selesaikan bab ini

  1. 1. Baca materi
  2. 2. Latihan kode (1)
  3. 3. Kuis (4 soal)

Latihan kode: Update daftar tugas secara immutable

Di komponen daftar tugas, state-nya berupa array seperti [{ id: 1, judul: "Belajar JSX", selesai: false }]. Setiap aksi dipanggil lewat updater, misalnya setTugas(prev => tambahTugas(prev, 3, "Ngoding")).

Lengkapi fungsi murni berikut. Jangan ubah array atau object asli, selalu kembalikan yang baru:

  1. tambahTugas(list, id, judul) menambah { id, judul, selesai: false } di akhir.
  2. toggleTugas(list, id) membalik selesai pada tugas dengan id itu, tugas lain tetap object yang sama.
  3. hapusTugas(list, id) membuang tugas dengan id itu.
  4. sisaTugas(list) mengembalikan jumlah tugas yang belum selesai (dipakai untuk teks "3 tugas tersisa").

Petunjuk: spread, map, filter.

Cek pemahaman

  1. 1Tombol di bawah diklik satu kali (awalnya n = 0). Angka berapa yang tampil setelah render ulang?

    function Penghitung() {
      const [n, setN] = useState(0);
    
      function klik() {
        setN(n + 1);
        setN(n + 1);
        setN((prev) => prev + 1);
      }
    
      return <button onClick={klik}>{n}</button>;
    }
  2. 2Apa akibat menulis <button onClick={hapus()}>Hapus</button>?

  3. 3Menjalankan daftar.push(item) lalu setDaftar(daftar) adalah cara yang benar untuk menambah item ke state array.

  4. 4Sebuah event handler memanggil tiga fungsi set state berbeda berturut-turut. Berapa kali komponen dirender ulang?