Reactive Declarations — Svelte

Reactive declarations ($:) memungkinkan kamu membuat nilai turunan yang otomatis terupdate. Reactive Statement let count = 0 $: doubled = count * 2 $…

Reactive declaration adalah baris di <script> yang diawali $:. Svelte menjalankan ulang baris itu setiap kali variabel yang disebut di dalamnya berubah. Bayangin buku kasir Warung Bu Sri yang dibuat di spreadsheet. Kolom total berisi rumus =harga*porsi. Begitu angka porsi diganti, kolom total ikut berubah sendiri, tanpa ada yang menghitung ulang pakai kalkulator. $: adalah cara menulis rumus seperti itu di komponen Svelte.

🧪 Tebakan cepat: harga Es Teh 5000 dan porsi mulai dari 1. Totalnya dihitung lewat function: function hitungTotal() { return harga * porsi }, lalu $: total = hitungTotal(). Tombol +1 Es Teh diklik dua kali. Halaman menampilkan Total: Rp 15.000 atau tetap Total: Rp 5.000? Jawabannya ada di bagian "Yang dilacak cuma yang tertulis".

Nilai yang ikut berubah: $: total = harga * porsi

Di pelajaran Reaktivitas, let total = keranjang.length cuma dihitung sekali. Dengan $:, nilai turunan seperti total selalu ikut sumbernya:

<script>
  let harga = 5000
  let porsi = 1
  $: total = harga * porsi
</script>

<button on:click={() => porsi += 1}>+1 Es Teh</button>
<p>Total: Rp {total.toLocaleString("id-ID")}</p>
Kejadian porsi total Tampil
Awal 1 5000 Total: Rp 5.000
Klik +1 Es Teh 2 10000 Total: Rp 10.000
Klik lagi 3 15000 Total: Rp 15.000

Yang dilakukan compiler

Svelte 4.2.19 mengubah baris $: tadi jadi kode update:

$self.$.update = () => {
  if ($self.$.dirty & /*porsi*/ 1) {
    $: $invalidate(1, total = harga * porsi);
  }
};

Latihan: doubled dari count

Latihannya memakai pola yang sama dengan angka yang lebih kecil:

<script>
  let count = 2
  $: doubled = count * 2
</script>

<p>{count} x 2 = {doubled}</p>

Tampilannya 2 x 2 = 4. Kalau nanti count diberi nilai 7, baris $: jalan lagi dan doubled otomatis jadi 14, tanpa kamu menulis doubled = 14 di mana pun.

$: untuk menjalankan kode

$: juga bisa dipakai di depan perintah biasa, bukan cuma untuk membuat nilai. Bentuk ini disebut reactive statement:

<script>
  let porsi = 1

  $: console.log("porsi sekarang:", porsi)

  $: if (porsi > 5) {
    console.log("Pesanan besar!")
  }
</script>

Console setelah halaman dibuka lalu porsi naik dua kali:

porsi sekarang: 1
porsi sekarang: 2
porsi sekarang: 3

Yang dilacak cuma yang tertulis

Ini kode dari tebakan:

<script>
  let harga = 5000
  let porsi = 1

  function hitungTotal() {
    return harga * porsi
  }

  $: total = hitungTotal()
</script>

Setelah dua klik, tampilannya tetap Total: Rp 5.000. Itu jawaban tebakannya. Compiler cuma membaca nama yang tertulis langsung di baris $:, yaitu hitungTotal, dan gak membuka isi function-nya. hitungTotal sendiri gak pernah berubah, jadi baris ini dianggap gak punya dependency. Hasil compile-nya $: $invalidate(1, total = hitungTotal()); tanpa if (dirty ...): jalan sekali saat komponen dibuat, lalu gak pernah lagi.

Cara membetulkannya, sebut dependency-nya di baris $::

function hitungTotal(h, p) {
  return h * p
}

$: total = hitungTotal(harga, porsi)

Sekarang porsi tertulis di baris itu, jadi compiler memasang if ($self.$.dirty & /*porsi*/ 1) di sekelilingnya, dan totalnya ikut naik.

📌 Urutan baris $:

Baris $: boleh saling bergantung. Svelte mengurutkannya sendiri berdasarkan siapa butuh siapa, bukan berdasarkan urutan kamu menulis:

$: bayar = total - diskon
$: diskon = total >= 20000 ? 2000 : 0
$: total = harga * porsi

Compiler menyusun ulang jadi total, lalu diskon, lalu bayar. Saat porsi jadi 4: total 20000, diskon 2000, bayar 18000. Kalau urutannya gak disusun ulang, bayar akan dihitung dari total yang belum diperbarui.

Svelte 5: $derived dan $effect

Di Svelte 5 dengan runes, $: dipecah jadi dua rune:

<script>
  let harga = 5000
  let porsi = $state(1)
  let total = $derived(harga * porsi)

  $effect(() => {
    console.log("porsi sekarang:", porsi)
  })
</script>

<button onclick={() => porsi += 1}>+1 Es Teh</button>
<p>Total: Rp {total.toLocaleString("id-ID")}</p>

⚠️ Jebakan yang sering ketemu

TL;DR: $: total = harga * porsi membuat nilai turunan yang dihitung ulang setiap variabel yang tertulis di baris itu diberi nilai baru. Semua $: jalan sekali sebelum render pertama, lalu tiap dependency-nya berubah. $: di depan perintah biasa (console.log, if, blok {}) menjalankan ulang perintah itu. Compiler cuma melihat nama yang tertulis langsung di baris $:, jadi variabel yang dibaca di dalam function gak dilacak; kirim sebagai argumen. Baris $: yang saling bergantung diurutkan otomatis. Latihannya: let count = 2, $: doubled = count * 2, <p>{count} x 2 = {doubled}</p> menampilkan 2 x 2 = 4. Di Svelte 5, pakai $derived untuk nilai dan $effect untuk efek samping.

Yang akan kamu pelajari