Proyek Solid — SolidJS

Saatnya membangun proyek mini menggunakan SolidJS! Spesifikasi: Todo App Tambah todo baru Tandai selesai/belum Hapus todo Filter: semua, aktif, selesai…

Proyek Solid menyatukan semua yang sudah kamu latih satu per satu: komponen, props, signal, memo, Show, For, dan event, jadi satu aplikasi yang dipasang ke halaman lewat satu pintu, src/index.jsx. Bayangin hari pembukaan Toko Buku Kak Nadia. Rak, label harga, mesin kasir, dan buku titipan sudah disiapkan sendiri-sendiri. Hari ini semuanya ditata jadi satu toko, lalu pintu depannya dibuka sekali. Sesudah itu, yang berubah cuma bagian kecil: angka di mesin kasir, satu baris di daftar titipan.

🧪 Tebakan cepat: App toko menulis console.log("App jalan") di badannya. Kamu menambah tiga buku ke keranjang, menghapus satu, lalu mengetik bumi di kotak cari. Berapa kali App jalan tercetak? Jawabannya ada di bagian "App jalan sekali".

Struktur proyek

Proyek Solid biasa dibuat dengan Vite: npm create vite@latest toko-buku -- --template solid. Isinya kira-kira:

toko-buku/
├── index.html          ← punya <div id="root"></div>
├── package.json
├── vite.config.js
└── src/
    ├── index.jsx       ← pintu masuk: render(...)
    ├── App.jsx
    └── components/
        ├── Header.jsx
        └── KartuBuku.jsx

index.jsx: render dan dispose

// src/index.jsx
import { render } from "solid-js/web"
import App from "./App"

const dispose = render(() => <App />, document.getElementById("root"))

App toko: semua konsep di satu tempat

// src/App.jsx
import { createSignal, createMemo, For, Show } from "solid-js"

const katalog = [
  { id: 1, judul: "Laskar Pelangi", harga: 89000, stok: 4 },
  { id: 2, judul: "Bumi Manusia", harga: 120000, stok: 2 },
  { id: 3, judul: "Cantik Itu Luka", harga: 95000, stok: 0 },
]
const rupiah = (n) => "Rp" + n.toLocaleString("id-ID")

function KartuBuku(props) {
  return (
    <li>
      {props.buku.judul} · {rupiah(props.buku.harga)}
      <Show when={props.buku.stok > 0} fallback={<span> · Habis</span>}>
        <button onClick={() => props.onTambah(props.buku)}>Tambah</button>
      </Show>
    </li>
  )
}

export default function App() {
  console.log("App jalan")
  const [cari, setCari] = createSignal("")
  const [keranjang, setKeranjang] = createSignal([])

  const hasil = createMemo(() => katalog.filter((b) => b.judul.toLowerCase().includes(cari().toLowerCase())))
  const total = createMemo(() => keranjang().reduce((t, b) => t + b.harga, 0))

  const tambah = (buku) => setKeranjang((k) => [...k, { ...buku, kode: Date.now() + Math.random() }])
  const hapus = (kode) => setKeranjang((k) => k.filter((b) => b.kode !== kode))

  return (
    <div>
      <p>Keranjang: {keranjang().length} buku · {rupiah(total())}</p>
      <input placeholder="Cari judul" onInput={(e) => setCari(e.target.value)} />
      <ul>
        <For each={hasil()}>{(b) => <KartuBuku buku={b} onTambah={tambah} />}</For>
      </ul>
      <ul>
        <For each={keranjang()}>
          {(b) => <li>{b.judul} <button onClick={() => hapus(b.kode)}>Hapus</button></li>}
        </For>
      </ul>
    </div>
  )
}
Bagian Alat Dari pelajaran
isi kotak cari, isi keranjang createSignal Signals
hasil pencarian, total harga createMemo Memos
daftar buku, baris keranjang For List Rendering
tulisan Habis untuk stok 0 Show Conditional Rendering
KartuBuku menerima buku dan onTambah props Props

Form: titip judul yang belum ada

Pembeli boleh menitip judul yang belum dijual. Ini pola kedua latihan:

const [titipan, setTitipan] = createSignal([])
const [judulTitip, setJudulTitip] = createSignal("")
const jumlahTitipan = createMemo(() => titipan().length)

const titip = (e) => {
  e.preventDefault()
  if (!judulTitip().trim()) return
  setTitipan((t) => [...t, { id: Date.now(), judul: judulTitip() }])
  setJudulTitip("")
}

<form onSubmit={titip}>
  <input value={judulTitip()} onInput={(e) => setJudulTitip(e.target.value)} />
  <button type="submit">Titip</button>
</form>
<p>{jumlahTitipan()} judul dititip</p>
<ul>
  <For each={titipan()}>{(t) => <li>{t.judul}</li>}</For>
</ul>

App jalan sekali

App jalan tercetak sekali, saat halaman dibuka. Itu jawaban tebakannya. Diuji di Solid 1.9: tiga kali Tambah, satu Hapus, dan mengetik di kotak cari gak menjalankan App lagi. Yang jalan ulang cuma bagian yang membaca signal yang berubah: total dan teks keranjang saat keranjang berubah, hasil dan daftar buku saat cari berubah.

⚠️ Jebakan yang sering ketemu

TL;DR: Proyek Solid dimulai dari src/index.jsx (atau index.tsx): render(() => <App />, document.getElementById("root")) dari "solid-js/web", yang mengembalikan dispose function untuk membongkar aplikasinya. App menyusun semuanya: createSignal untuk isian dan daftar, createMemo untuk turunan seperti total dan jumlah, For untuk daftar, Show untuk syarat, props untuk komponen kecil. Form memakai onSubmit + e.preventDefault(), mengabaikan isian kosong, menambah dengan [...lama, baru], mengosongkan kotaknya, dan menghapus dengan filter. App jalan sekali; sesudahnya cuma bagian yang membaca signal yang berubah yang diperbarui.

Yang akan kamu pelajari