Memos — SolidJS

Memos adalah computed value yang di-cache — hanya dihitung ulang saat dependency berubah. createMemo import { createSignal, createMemo } from "solid-js"…

Memo adalah nilai turunan yang di-cache: nilai yang dihitung dari signal lain, disimpan, dan cuma dihitung ulang kalau signal sumbernya berubah. createMemo(() => count() * 2) mengembalikan getter, jadi dibaca seperti signal: doubled(). Bayangin Kak Nadia mencari buku yang judulnya mengandung kata tertentu, lalu menulis hasilnya di papan kecil dekat kasir. Siapa pun yang bertanya cukup membaca papan itu. Kak Nadia baru menyusuri rak lagi kalau kata pencariannya berganti. Fungsi biasa itu kebalikannya: tiap orang yang bertanya membuat Kak Nadia menyusuri rak dari awal.

🧪 Tebakan cepat: daftar hasil pencarian dibuat dengan fungsi biasa, const hasil = () => { console.log("filter jalan"); return buku.filter(...) }, dan dibaca di dua paragraf. Kamu mengetik satu huruf di kotak cari. filter jalan tercetak berapa kali, 1 atau 2? Jawabannya ada di bagian "Fungsi biasa vs createMemo".

createMemo: hitung sekali, baca berkali-kali

import { createSignal, createMemo } from "solid-js"

function App() {
  const [count, setCount] = createSignal(3)
  const doubled = createMemo(() => count() * 2)

  return (
    <div>
      <p>{count()} x 2 = {doubled()}</p>
    </div>
  )
}

Hasilnya <p>3 x 2 = 6</p>.

Itu latihan pertama: count mulai dari 3, doubled sebagai memo, dan paragraf yang merangkai keduanya.

Fungsi biasa vs createMemo

Kotak pencarian Toko Buku Kak Nadia, dengan dua nilai turunan: hasil pencarian dan total keranjang.

import { createSignal, createMemo } from "solid-js"

const buku = [
  { judul: "Laskar Pelangi", harga: 89000 },
  { judul: "Bumi Manusia", harga: 120000 },
  { judul: "Cantik Itu Luka", harga: 95000 },
]

function App() {
  const [cari, setCari] = createSignal("")
  const [jumlah, setJumlah] = createSignal(0)

  const hasil = createMemo(() => {
    console.log("filter jalan")
    return buku.filter(b => b.judul.toLowerCase().includes(cari().toLowerCase()))
  })
  const total = createMemo(() => {
    console.log("total dihitung")
    return jumlah() * 89000
  })

  return (
    <div>
      <input value={cari()} onInput={e => setCari(e.target.value)} placeholder="Cari judul" />
      <p>Hasil: {hasil().map(b => b.judul).join(", ")}</p>
      <p>{hasil().length} buku ditemukan</p>
      <button onClick={() => setJumlah(j => j + 1)}>+1 Buku</button>
      <p>Total: Rp{total()}</p>
      <p>Gratis ongkir: {total() >= 150000 ? "ya" : "belum"}</p>
    </div>
  )
}

hasil() dibaca di dua paragraf, total() juga. Ini isi console-nya, dibandingkan dengan versi fungsi biasa (const hasil = () => { ... } dan const total = () => { ... }), diuji di Solid 1.9:

Kejadian Dengan createMemo Dengan fungsi biasa
Halaman dibuka filter jalan 1×, total dihitung 1× filter jalan 2×, total dihitung 2×
Ketik b filter jalan 1× filter jalan 2×
Klik +1 Buku total dihitung 1× total dihitung 2×

Fungsi biasa jalan sekali per tempat yang membacanya, jadi satu huruf membuat filter jalan tercetak 2 kali. Itu jawaban tebakannya. Memo jalan sekali per perubahan, berapa pun yang membacanya. Hasilnya sama di layar: Hasil: Bumi Manusia, 1 buku ditemukan. Yang beda cuma berapa kali kerjanya diulang.

Perhatikan juga: mengetik gak menjalankan total, dan klik gak menjalankan filter. Masing-masing cuma mengikuti signal yang dibacanya.

Signal, memo, atau effect?

Pakai Untuk Contoh
createSignal nilai yang kamu ubah sendiri lewat setter isi kotak cari, jumlah di keranjang
createMemo nilai yang dihitung dari signal lain, read-only hasil filter, total harga
createEffect kerja di luar tampilan simpan ke localStorage, document.title

Fungsi biasa seperti () => harga() * 0.9 tetap boleh untuk hitungan murah yang dibaca di satu tempat. Begitu hitungannya berat atau dibaca di banyak tempat, jadikan memo.

Latihan kedua: ganjil atau genap

import { createSignal, createMemo } from "solid-js"

function App() {
  const [count, setCount] = createSignal(5)
  const isEven = createMemo(() => count() % 2 === 0)

  return (
    <div>
      <p>{count()} is {isEven() ? "Genap" : "Ganjil"}</p>
      <button onClick={() => setCount(c => c + 1)}>+1</button>
    </div>
  )
}

💡 Memo cuma memberi tahu kalau nilainya berubah

Memo menyimpan hasil terakhir dan membandingkannya dengan hasil baru. Kalau sama, gak ada yang diberi tahu. Misalnya const adaStok = createMemo(() => stok() > 0): stok turun dari 4 ke 3, memonya menghitung ulang, hasilnya tetap true, jadi teks yang membaca adaStok() gak disentuh. Baru saat stok jadi 0 hasilnya berubah ke false dan teksnya ikut.

⚠️ Jebakan yang sering ketemu

TL;DR: const doubled = createMemo(() => count() * 2) membuat nilai turunan yang di-cache: dihitung sekali saat dibuat, dihitung ulang cuma saat signal sumbernya berubah, lalu dibaca sebagai {doubled()} berapa kali pun tanpa menghitung lagi. Fungsi biasa jalan sekali per tempat yang membacanya: hasil filter yang dibaca di dua paragraf difilter dua kali per ketikan, dengan memo sekali. Memo read-only dan cuma memberi tahu pembacanya kalau hasilnya berubah. Signal untuk nilai yang kamu ubah, memo untuk nilai yang dihitung, effect untuk kerja di luar tampilan.

Yang akan kamu pelajari