Derived store adalah store yang nilainya dihitung dari store lain, dan dihitung ulang sendiri setiap sumbernya berubah. Bayangin layar kasir Warung Bu Sri yang tersambung ke keranjang. Setiap pelayan menambah pesanan, angka total di layar ikut berganti sendiri. Kasir gak bisa mengetik totalnya dengan tangan; kalau mau angkanya berubah, isi keranjangnya yang harus diubah. Di pelajaran Reactive Declarations, rumus seperti itu ditulis $: total = ... di dalam satu komponen. Derived store adalah rumus yang sama, tapi tinggal di stores.js dan dipakai banyak komponen.
🧪 Tebakan cepat:
totaladalah derived store darikeranjang. Kasir yang iseng menjalankantotal.set(0)supaya pesanannya gratis. Totalnya jadiRp 0, atau error? Jawabannya ada di bagian "Derived itu read-only".
derived: store dari store lain
Ini stores.js dari pelajaran Stores, ditambah total:
// stores.js
import { writable, derived } from "svelte/store"
export const keranjang = writable([])
export const total = derived(keranjang, ($keranjang) =>
$keranjang.reduce((jumlah, m) => jumlah + m.harga, 0)
)
derived(sumber, rumus)membuat store baru.deriveddiimpor dari"svelte/store", sama sepertiwritable.rumusmenerima isi sumbernya. Nama parameternya bebas; kebiasaannya diberi$di depan,$keranjang, sebagai pengingat bahwa itu isi store, bukan store-nya.- Yang dikembalikan
rumusjadi nilaitotal. Di sini: jumlah semua harga di keranjang.
Membaca derived store
Derived store dibaca persis seperti store lain, dengan $:
<!-- Kasir.svelte -->
<script>
import { total } from "./stores.js"
</script>
<p>Total: Rp {$total.toLocaleString("id-ID")}</p>
| Kejadian | Isi keranjang |
total |
Kasir |
|---|---|---|---|
| Awal | [] |
0 | Total: Rp 0 |
| Tambah Nasi Goreng | 1 item, 15000 | 15000 | Total: Rp 15.000 |
| Tambah Es Teh | 2 item, 15000 + 5000 | 20000 | Total: Rp 20.000 |
Gak ada satu baris pun yang menulis ke total. Tombol Tambah cuma memanggil keranjang.update(...), dan rumusnya dijalankan ulang sendiri.
Dari beberapa store sekaligus
Sumbernya boleh array. Rumusnya lalu menerima array isi, dengan urutan yang sama:
export const diskon = writable(0)
export const bayar = derived([total, diskon], ([$total, $diskon]) =>
$total - ($total * $diskon) / 100
)
Dengan keranjang berisi Nasi Goreng (total 15000), ini yang dijalankan Svelte 4 yang sebenarnya:
| Kejadian | Rumus total | Rumus bayar | $bayar |
|---|---|---|---|
| Tambah Nasi Goreng | jalan | jalan | 15000 |
diskon.set(10) |
gak jalan | jalan | 13500 |
diskon.set(10) lagi |
gak jalan | gak jalan | 13500 |
- Rumus cuma jalan kalau sumbernya sendiri berubah.
totalgak bergantung padadiskon, jadi diskon baru gak menyentuhnya. - Store yang diberi nilai yang sama (10 lalu 10) gak mengabari siapa pun, jadi rumus bayar juga diam.
Derived itu read-only
Derived store cuma punya subscribe. Gak ada set, gak ada update. Jadi total.set(0) berakhir dengan:
TypeError: total.set is not a function
Totalnya tetap Rp 15.000, bukan Rp 0. Itu jawaban tebakannya. Nilai derived store selalu hasil rumus dari sumbernya; kalau mau totalnya berubah, ubah keranjang.
📌 Dihitung cuma kalau ada yang membaca
Derived store baru bekerja saat punya pelanggan. Rumusnya jalan sekali ketika pelanggan pertama datang (misalnya komponen yang menulis {$bayar} dibuat), lalu tiap sumbernya berubah. Derived store yang gak dibaca komponen mana pun gak pernah menjalankan rumusnya, berapa kali pun keranjangnya diubah. Jadi derived store yang mahal dihitung aman ditaruh di stores.js: dia cuma bekerja kalau dipakai.
Latihan: doubled
Latihannya memakai angka kecil. Kode awal sudah punya count = writable(5):
<script>
import { writable, derived } from "svelte/store"
const count = writable(5)
const doubled = derived(count, ($c) => $c * 2)
</script>
<p>{$count} x 2 = {$doubled}</p>
- Paragrafnya berbunyi
5 x 2 = 10. Kedua angkanya dibaca dari store:{$count}dan{$doubled}. - Menulis
<p>5 x 2 = 10</p>memang menampilkan teks yang sama, tapi kalimatnya berhenti mengikuti store. Ganticountjadiwritable(7), dan yang tampil tetap10. Penguji mencariderived(dan{$doubled}di kodemu karena itu.
Svelte 5
Derived store tetap jalan di Svelte 5. Untuk nilai turunan di dalam satu komponen, Svelte 5 memakai rune $derived: let total = $derived(keranjang.reduce((jumlah, m) => jumlah + m.harga, 0)), dengan keranjang dibuat dari $state. Derived store tetap pilihan yang pas untuk nilai turunan yang dipakai banyak komponen lewat stores.js.
⚠️ Jebakan yang sering ketemu
- Mengubah derived store langsung.
total.set(0)gagal denganTypeError: total.set is not a function. Ubah sumbernya. - Angka yang diketik sendiri.
<p>5 x 2 = 10</p>kebetulan benar sekarang, tapi gak ikut berubah. Baca dari store:{$count} x 2 = {$doubled}. - Lupa
$di template.{doubled}menampilkan[object Object], sama seperti store biasa. - Rumus yang membaca store tanpa menyebutnya sebagai sumber. Derived store cuma dihitung ulang saat store di argumen pertamanya berubah. Kalau rumus bayar butuh
diskon,diskonharus ada di array sumbernya.
TL;DR: derived(sumber, rumus) dari "svelte/store" membuat store yang nilainya dihitung dari store lain, misalnya total = derived(keranjang, ($keranjang) => ...jumlah harga). Rumusnya jalan ulang sendiri tiap sumbernya berubah, dan dibaca dengan {$total} seperti store lain. Sumbernya bisa array: derived([total, diskon], ([$total, $diskon]) => ...), dan diskon baru cuma menjalankan ulang rumus bayar. Derived store read-only: total.set(0) menghasilkan TypeError: total.set is not a function, jadi jawabannya "tidak bisa di-set". Rumusnya cuma jalan kalau ada pelanggan. Latihannya: const doubled = derived(count, ($c) => $c * 2) dan <p>{$count} x 2 = {$doubled}</p>, yang menampilkan 5 x 2 = 10.