Reactivity Lanjutan — SolidJS

Solid menyediakan primitives lanjutan untuk kontrol reaktivitas yang lebih fine-grained. batch import { batch } from "solid-js" batch(() => { setCount(1)…

Solid mencatat siapa membaca signal apa, lalu menjalankan ulang yang perlu saja. Pelajaran ini memberi alat untuk mengatur catatan itu: batch menggabungkan beberapa perubahan jadi satu putaran, untrack dan on memilih apa yang dilacak, createRoot membuat pemilik yang kamu bongkar sendiri, dan createSelector serta createDeferred menjaga daftar besar tetap ringan. Bayangin mesin kasir Toko Buku Kak Nadia. Kalau struk dicetak tiap satu barang di-scan, pembeli yang membeli dua buku dapat struk setengah jadi di tengah jalan. Kasir yang benar men-scan semuanya dulu, baru mencetak satu struk.

🧪 Tebakan cepat: tombol Update menjalankan setCount(c => c + 1) lalu setName("Budi"), tanpa batch. Ada efek createEffect(() => console.log(name() + ": " + count())). Satu klik dari Andi: 0, apa yang tercetak? Jawabannya ada di bagian "batch: satu putaran untuk banyak perubahan".

batch: satu putaran untuk banyak perubahan

Bentuk latihannya, ditambah satu efek pencatat:

import { createSignal, createEffect, batch } from "solid-js"

function App() {
  const [count, setCount] = createSignal(0)
  const [name, setName] = createSignal("Andi")

  createEffect(() => console.log(name() + ": " + count()))

  const updateAll = () => {
    batch(() => {
      setCount((c) => c + 1)
      setName("Budi")
    })
  }

  return (
    <div>
      <p>{name()}: {count()}</p>
      <button onClick={updateAll}>Update</button>
    </div>
  )
}

Diuji di Solid 1.9, satu klik dari Andi: 0:

updateAll Console Efek jalan
tanpa batch Andi: 1, lalu Budi: 1 2 kali
dengan batch Budi: 1 1 kali

Tanpa batch, tercetak Andi: 1 lalu Budi: 1. Itu jawaban tebakannya. Tiap setter langsung menjalankan pembacanya, jadi efeknya sempat melihat Andi: 1, keadaan setengah jadi yang gak pernah dimaksud.

untrack: membaca tanpa berlangganan

createEffect(() => console.log(untrack(name) + ": " + count()))

Diuji: setName("Budi") gak menjalankan efeknya. setCount(1) sesudahnya mencetak Budi: 1. untrack(fn) memanggil fn tanpa mencatat bacaannya, jadi efek cuma jalan ulang karena count, tapi tetap membaca nama terbaru saat jalan. Signal itu fungsi, jadi untrack(name) cukup, sama dengan untrack(() => name()).

on: menyebut sumbernya sendiri

createEffect(on(count, (nilai, sebelumnya) => {
  console.log(sebelumnya, "->", nilai, "· nama:", name())
}))

createRoot dan pemilik

Tiap efek dan memo punya pemilik (owner): komponen atau root tempat dia dibuat. Saat pemiliknya dibongkar, efeknya berhenti dan onCleanup-nya jalan. Komponen dibongkar Solid. createRoot membuat pemilik yang kamu bongkar sendiri:

const [count, setCount] = createSignal(0)

const dispose = createRoot((dispose) => {
  createEffect(() => console.log("efek:", count()))   // efek: 0
  onCleanup(() => console.log("dibersihkan"))
  return dispose
})

setCount(1)   // efek: 1
dispose()     // dibersihkan
setCount(2)   // gak tercetak apa pun

Daftar besar: createSelector dan createDeferred

Rak 1.000 buku, dan buku yang dipilih ditandai:

const [dipilih, setDipilih] = createSignal(1)
const isDipilih = createSelector(dipilih)

<For each={rak}>
  {(b) => <li classList={{ pilih: isDipilih(b.id) }}>{b.judul}</li>}
</For>
Tiap baris membaca Pilih buku lain: yang dihitung ulang
dipilih() === b.id 1.000 baris
isDipilih(b.id) 2 baris (yang lama dan yang baru)

Diuji di Solid 1.9 dengan 1.000 efek. createSelector(sumber) mencatat pembacanya per kunci, jadi perubahan cuma membangunkan baris yang hasilnya berubah.

createDeferred(cari, { timeoutMs: 500 }) memberi salinan cari yang menyusul belakangan, saat browser sedang longgar, paling lambat 500 ms. Diuji: tepat sesudah cari diubah tiga kali jadi bumi, salinannya masih "", lalu berubah sekali jadi bumi. Filter rak yang berat membaca salinannya, jadi kotak cari tetap lancar diketik.

⚠️ Jebakan yang sering ketemu

TL;DR: batch(() => { setCount((c) => c + 1); setName("Budi") }) menjalankan efek yang terpengaruh sekali, sesudah semua perubahan; tanpa batch, efeknya jalan dua kali dan sempat melihat Andi: 1. untrack(name) membaca tanpa melacak, dan on(count, (nilai, sebelumnya) => ...) cuma melacak sumber yang disebut (defer: true melewati jalan pertama). Tiap efek punya pemilik; createRoot((dispose) => ...) membuat pemilik yang dibongkar dengan dispose(), dan efek di dalam setTimeout butuh runWithOwner. Untuk daftar besar, createSelector membuat pilihan baru cuma menghitung ulang 2 baris, bukan 1.000, dan createDeferred menunda nilai untuk kerja berat.

Yang akan kamu pelajari