Store Mutations — SolidJS

produce dan reconcile mempermudah update store yang kompleks. produce (Immer-like) import { produce } from "solid-js/store" setState(produce(state => {…

Store cuma bisa diubah lewat setter-nya. Untuk perubahan yang lebih dari satu langkah, "solid-js/store" punya dua pembantu: produce, yang membiarkan kamu menulis perubahan seperti mengubah objek biasa lalu menerapkan semuanya sekaligus, dan reconcile, yang memasang data baru dari server tanpa membuang objek yang masih sama. Bayangin Kak Nadia dan petugas rak. Kalau tiap koreksi diteriakkan satu per satu, petugas rak bolak-balik merapikan rak sesudah tiap teriakan. produce itu menulis semua koreksi di satu catatan lalu menyerahkannya sekali. Lalu tiap pagi pemasok mengirim katalog baru. Kak Nadia gak membuang semua kartu dan mencetak ulang. Dia mencocokkan katalog baru dengan kartu yang ada lewat kode bukunya, dan cuma mengganti angka yang beda. Itu reconcile.

🧪 Tebakan cepat: tombol Pesan Laskar Pelangi memanggil setToko dua kali: stoknya dikurangi, lalu judulnya ditambahkan ke keranjang. Sebuah efek mencetak ringkasan: ... stok, ... di keranjang. Satu klik, berapa baris ringkasan yang tercetak: 1 atau 2? Jawabannya ada di bagian "Satu klik, berapa kali update".

Store dibaca dari mana saja, diubah lewat setter

const [state, setState] = createStore({ items: [1, 2, 3] })
state.items.push(4)

Ini gak menambah apa pun: state.items tetap [1, 2, 3]. Store bersifat read-only dari luar setter-nya, jadi push di atas gagal tanpa error, dan di mode development console menulis Cannot mutate a Store directly dua kali (sekali untuk items[3], sekali untuk length). Yang benar: lewat setState, atau produce di dalamnya.

produce: tulis seperti objek biasa

import { createStore, produce } from "solid-js/store"

function App() {
  const [state, setState] = createStore({ items: ["A", "B"] })

  const addItem = () => {
    setState(produce(s => {
      s.items.push("C")
    }))
  }

  return (
    <div>
      <p>Items: {state.items.join(", ")}</p>
      <button onClick={addItem}>Tambah</button>
    </div>
  )
}
Kejadian state.items Paragraf
Halaman dibuka ["A", "B"] Items: A, B
Klik Tambah ["A", "B", "C"] Items: A, B, C

Satu klik, berapa kali update

Toko Buku Kak Nadia dengan keranjang di store dan efek ringkasan:

createEffect(() => {
  console.log("ringkasan:", toko.buku[0].stok, "stok,", toko.keranjang.length, "di keranjang")
})

// Versi A: dua setter
const pesan = () => {
  setToko("buku", 0, "stok", s => s - 1)
  setToko("keranjang", k => [...k, toko.buku[0].judul])
}

// Versi B: satu produce
const pesan = () => {
  setToko(produce(t => {
    t.buku[0].stok -= 1
    t.keranjang.push(t.buku[0].judul)
  }))
}

Diuji di Solid 1.9, klik Pesan Laskar Pelangi sekali:

Versi Console
A, dua setToko ringkasan: 3 stok, 0 di keranjang, lalu ringkasan: 3 stok, 1 di keranjang
B, satu produce ringkasan: 3 stok, 1 di keranjang

Dua setter, dua update. Itu jawaban tebakannya. Solid 1.9 gak mengumpulkan setter di dalam handler klik: tiap setToko langsung menjalankan semua yang membaca bagian itu. Di antara keduanya ada keadaan setengah jadi, stoknya sudah turun tapi keranjangnya belum bertambah, dan efeknya sempat melihatnya. produce menerapkan semua perubahannya dalam satu update. Untuk beberapa setter terpisah, signal juga, bungkus dengan batch dari "solid-js": batch(() => { setToko(...); setToko(...) }) juga cuma mencetak satu baris.

reconcile: data baru, objek lama

Katalog baru dari server: Laskar Pelangi tetap 4, Bumi Manusia jadi 5, Cantik Itu Luka jadi 3, ditambah Ronggeng Dukuh Paruk. Tiap buku punya id. Ada dua cara memasangnya:

import { reconcile } from "solid-js/store"

setToko("buku", ambilKatalog())             // ganti
setToko("buku", reconcile(ambilKatalog()))  // cocokkan

Dengan catatan kado di kartu Laskar Pelangi, diuji di Solid 1.9:

ganti reconcile
Baris yang dibuat 4, semua kartu dibuat ulang 1, cuma Ronggeng Dukuh Paruk
Teks yang diganti semua, lewat baris baru 3, cuma stok yang berubah
Catatan kado hilang tetap

⚠️ Jebakan yang sering ketemu

TL;DR: Store gak bisa diubah langsung (state.items.push(4) gak berpengaruh, console development menulis Cannot mutate a Store directly). setState(produce(s => { s.items.push("C") })) membiarkan kamu menulis perubahan seperti objek biasa, lalu menerapkannya dalam satu update. Dua setToko berturut-turut di handler klik berarti dua update, dengan keadaan setengah jadi di antaranya; produce atau batch membuatnya satu. setToko("buku", reconcile(dataBaru)) memasang data dari server dengan mencocokkan id: objek yang sama dipertahankan, cuma yang beda diganti, jadi baris dan isinya tetap.

Yang akan kamu pelajari