Watchers — Vue

watch() — Mengamati Perubahan const query = ref("") watch(query, (newVal, oldVal) => { console.log("Query berubah:", oldVal, "→", newVal) }) ---template--- {{…

Watcher adalah fungsi yang dijalankan Vue setiap kali data reaktif tertentu berubah. Dia dipakai untuk efek samping (side effect): pekerjaan di luar menghitung nilai untuk ditampilkan, misalnya menyimpan keranjang ke localStorage, menulis ke console, atau memanggil server. Bayangin Bu Sri berpesan ke pelayan: "Setiap kali angka pesanan di papan berubah, salin angkanya ke buku catatan." Pelayan gak menghitung apa-apa. Dia cuma menunggu papannya berubah, lalu bertindak. Papan itu data reaktif, pelayannya watcher.

🧪 Tebakan cepat: keranjang = reactive({ menu: "Es Teh", jumlah: 1 }), lalu watch(keranjang.jumlah, (baru) => localStorage.setItem("jumlah", baru)). Pembeli menekan +1 dua kali, layarnya jadi 3. Apa isi localStorage untuk "jumlah"? Tebak dulu, jawabannya ada di bagian "Sumber yang bisa diawasi".

watch(): jalan saat sumbernya berubah

const jumlah = ref(Number(localStorage.getItem("jumlah") ?? 1))

watch(jumlah, (baru, lama) => {
  localStorage.setItem("jumlah", baru)
  console.log("Disimpan:", lama, "→", baru)
})

---template---
<p>Es Teh × {{ jumlah }} · Rp{{ jumlah * 5000 }}</p>
<button @click="jumlah++">+1 porsi</button>
Kejadian jumlah Console localStorage "jumlah"
Halaman tampil 1 (kosong) (belum ada)
Klik +1 porsi 2 Disimpan: 1 → 2 "2"
Klik +1 porsi 3 Disimpan: 2 → 3 "3"
Muat ulang halaman 3 (kosong) "3"

Latihan pertama memakai pola yang sama: watch(count, (newVal) => { console.log("Count:", newVal) }). Klik tombolnya sekali, angka jadi 1 dan console menulis Count: 1. Latihan kedua mengawasi status dari sebuah <select> dan memakai nilai lamanya: ganti dari Buka ke Tutup, paragrafnya jadi Buka ke Tutup.

watch atau computed?

computed watch
Gunanya menghitung nilai untuk ditampilkan melakukan sesuatu saat data berubah
Mengembalikan nilai (ref yang cuma bisa dibaca) gak mengembalikan nilai untuk template
Contoh warung total = computed(() => jumlah.value * 5000) menyimpan jumlah ke localStorage

Pegangannya: kalau hasilnya mau ditampilkan, pakai computed. Kalau mau melakukan sesuatu, pakai watcher. Teks seperti "Mencari: " + query adalah nilai turunan dan cocok jadi computed. Teks yang butuh nilai lama, seperti Buka ke Tutup di latihan kedua, gak bisa dibuat computed: computed cuma tahu nilai sekarang, sedangkan callback watch menerima (baru, lama).

watchEffect(): jalan langsung, sumbernya dicatat sendiri

watchEffect(() => {
  localStorage.setItem("jumlah", jumlah.value)
  console.log("Disimpan:", jumlah.value)
})
watch watchEffect
Sumber ditulis eksplisit dicatat otomatis dari yang dibaca
Jalan pertama kali saat sumbernya berubah langsung, waktu dibuat
Nilai lama ada gak ada

watch juga bisa jalan langsung dengan opsi { immediate: true }. Panggilan pertamanya menerima lama = undefined.

Sumber yang bisa diawasi

const keranjang = reactive({ menu: "Es Teh", jumlah: 1 })

watch(keranjang.jumlah, simpan)        // salah: yang dikirim angka 1
watch(() => keranjang.jumlah, simpan)  // benar: getter
watch(keranjang, simpan)               // seluruh object, deep
Sumber Contoh Watcher jalan saat
ref jumlah jumlah.value berubah
getter () => keranjang.jumlah hasil fungsinya berubah
object reactive keranjang properti apa pun di dalamnya berubah, sedalam apa pun
array sumber [menu, jumlah] salah satunya berubah

keranjang.jumlah dibaca JavaScript sebelum watch dipanggil, jadi yang sampai ke watch cuma angka 1, bukan data reaktif. Angka gak bisa berubah, jadi watcher-nya gak pernah jalan, dan localStorage tetap kosong walau layarnya sudah 3. Itu jawaban tebakannya. Di build production gak ada pesan apa pun; build development menulis [Vue warn]: Invalid watch source: 1 A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types. Bungkus jadi getter: () => keranjang.jumlah.

Mengawasi seluruh keranjang itu deep otomatis: mengganti menu pun memicunya. Tapi baru dan lama adalah object yang sama, karena isinya diubah di tempat. Setelah jumlah naik dari 1 ke 2, lama.jumlah juga 2. Kalau butuh angka lamanya, awasi getter-nya.

📌 Opsi dan waktu jalannya

⚠️ Jebakan yang sering ketemu

TL;DR: Watcher menjalankan efek samping saat data reaktif berubah, sedangkan computed menghitung nilai untuk ditampilkan. watch(jumlah, (baru, lama) => { ... }) butuh sumber eksplisit, lazy (gak jalan di awal), dan memberi nilai baru dan lama. watchEffect(() => { ... }) mencatat sendiri data yang dibacanya dan langsung jalan sekali. Sumber yang sah: ref, getter () => keranjang.jumlah, object reactive (deep, tapi lama sama dengan baru), atau array dari itu. keranjang.jumlah tanpa getter cuma angka, jadi watcher-nya gak pernah jalan. Opsi penting: immediate, deep, once, dan flush: "post" untuk membaca DOM yang sudah diperbarui.

Yang akan kamu pelajari