Resources — SolidJS

createResource menangani async data fetching dengan built-in loading/error states. createResource import { createResource, Suspense } from "solid-js" const…

createResource membungkus fungsi async yang mengambil data (disebut fetcher) dan memberimu dua hal: pembaca datanya, dan status pengambilannya, yaitu sedang memuat, gagal, atau sudah siap. Pasangannya <Suspense>, yang menampilkan tulisan pengganti selama datanya belum datang. Bayangin Kak Nadia memesan katalog dari pemasok lewat telepon. Selama menunggu, di rak dipasang papan Memuat katalog.... Slip pesanannya mencatat statusnya: masih menunggu, gagal, atau sudah sampai. Kalau pembeli minta judul lain, Kak Nadia menelepon lagi dengan kata yang baru.

🧪 Tebakan cepat: katalog Toko Buku Kak Nadia dimuat dengan createResource(cari, ambilKatalog) di dalam <Suspense fallback={<p>Memuat katalog...</p>}>, dan daftarnya membaca buku(). Katalog sudah tampil, lalu kamu mengetik bumi di kotak cari. Selama data barunya dimuat, daftar lama tetap tampil, atau papan Memuat katalog... muncul lagi? Jawabannya ada di bagian "Saat data dimuat ulang".

createResource dan Suspense

import { createResource, Suspense, Show } from "solid-js"

const fetchData = async () => {
  return { name: "Data Berhasil" }
}

function App() {
  const [data] = createResource(fetchData)

  return (
    <Suspense fallback={<p>Loading...</p>}>
      <Show when={data()}>
        <p>{data().name}</p>
      </Show>
    </Suspense>
  )
}
Saat data() Halaman
Komponen jalan, fetcher dipanggil undefined Loading...
Promise selesai { name: "Data Berhasil" } Data Berhasil

Status sebuah resource

Selain data(), resource punya properti status. Ini properti biasa, dibaca tanpa kurung:

Properti Isinya
data.loading true selama fetcher belum selesai
data.error objek error kalau fetcher gagal
data.state "pending" (pertama kali), "ready", "refreshing" (dimuat ulang), "errored"
data.latest data terakhir yang berhasil, tetap ada selama dimuat ulang

Latihan kedua menampilkan tiga keadaan itu dengan <Show>, tanpa <Suspense>:

import { createResource, Show } from "solid-js"

const fetchUser = async () => ({ name: "Andi", email: "[email protected]" })

function App() {
  const [user] = createResource(fetchUser)

  return (
    <div>
      <Show when={user.loading}><p>Loading...</p></Show>
      <Show when={user.error}><p>Error!</p></Show>
      <Show when={!user.error && user()}>
        <p>{user().name} - {user().email}</p>
      </Show>
    </div>
  )
}

Hasilnya Andi - [email protected]. Kenapa !user.error && di Show ketiga? Resource yang gagal melempar error-nya saat dibaca dengan user(). Diuji di Solid 1.9: tanpa penjaga itu, fetcher yang gagal menghentikan halaman dengan Error: Server sibuk, padahal Error! sudah siap ditampilkan.

Sumber: ambil ulang saat signal berubah

Katalog Toko Buku Kak Nadia yang mengikuti kotak cari. Sandbox gak punya jaringan, jadi "server"-nya Promise yang selesai 800 ms kemudian. Di aplikasi asli, isinya fetch("/api/buku?cari=" + cari).then(r => r.json()).

import { createSignal, createResource, For, Suspense } from "solid-js"

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

function ambilKatalog(cari) {
  console.log("fetcher jalan, cari:", JSON.stringify(cari))
  return new Promise(resolve => {
    setTimeout(() => resolve(katalog.filter(b => b.judul.toLowerCase().includes(cari.toLowerCase()))), 800)
  })
}

function App() {
  const [cari, setCari] = createSignal("")
  const [buku, { refetch }] = createResource(cari, ambilKatalog)

  return (
    <div>
      <input placeholder="Cari judul" onInput={e => setCari(e.target.value)} />
      <button onClick={refetch}>Muat ulang</button>
      <Suspense fallback={<p>Memuat katalog...</p>}>
        <ul>
          <For each={buku()}>{(b) => <li>{b.judul} · Rp{b.harga}</li>}</For>
        </ul>
      </Suspense>
    </div>
  )
}

Saat data dimuat ulang

Dimuat ulang (ketik di kotak cari atau klik Muat ulang), diuji di Solid 1.9:

Isi <Suspense> Selama memuat ulang Sesudahnya
<For each={buku()}> daftar hilang, muncul Memuat katalog... Bumi Manusia · Rp120000
<Show when={buku.loading}><p>Memperbarui...</p></Show> + <For each={buku.latest}> daftar lama tetap, plus Memperbarui... Bumi Manusia · Rp120000

Dengan buku(), papan Memuat katalog... muncul lagi tiap dimuat ulang. Itu jawaban tebakannya. buku() yang dibaca selama status "refreshing" membuat <Suspense> menunggu lagi. buku.latest mengembalikan data terakhir tanpa menunggu, jadi daftar lama tetap ada. Fallback cuma tampil saat pemuatan pertama.

Kalau fetcher gagal

Kalau promise-nya ditolak, buku.state jadi "errored", buku.error berisi error-nya, dan membaca buku() atau buku.latest melempar error itu ke <ErrorBoundary> terdekat:

<ErrorBoundary fallback={(err, reset) => (
  <p>Gagal: {err.message} <button onClick={() => { refetch(); reset() }}>Coba lagi</button></p>
)}>
  <Suspense fallback={<p>Memuat katalog...</p>}>...</Suspense>
</ErrorBoundary>

reset() cuma menggambar ulang isinya, jadi panggil refetch() juga, kalau gak, error yang sama langsung muncul lagi. ErrorBoundary dibahas lengkap di pelajaran Error Boundaries.

⚠️ Jebakan yang sering ketemu

TL;DR: const [data] = createResource(fetchData) memanggil fetcher async dan memberi data() (undefined sampai siap) plus properti data.loading, data.error, data.state, data.latest, dibaca tanpa kurung. <Suspense fallback={...}> menampilkan fallback selama resource yang dibaca di dalamnya belum siap; <Show when={data()}> menjaga bacaan isinya. createResource(cari, ambilKatalog) memanggil fetcher lagi tiap sumbernya berubah, refetch() memanggilnya lagi dengan sumber yang sama. Saat dimuat ulang, buku() memunculkan fallback lagi, buku.latest mempertahankan data lama. Resource yang gagal melempar error-nya saat dibaca: tangkap dengan ErrorBoundary, atau jaga dengan !user.error &&.

Yang akan kamu pelajari