Refs dan Directives — SolidJS

Refs memberikan akses langsung ke elemen DOM. Directives menambahkan custom behavior. Ref function App() { let inputRef onMount(() => inputRef.focus()) return…

Kadang kamu butuh elemen DOM aslinya di tangan: untuk memberi fokus ke kotak cari, menyeleksi isinya, atau mengukur lebarnya. Itu gunanya ref: atribut ref di JSX menyerahkan elemen itu ke kodemu. Directive selangkah lebih jauh: perilaku untuk elemen yang ditulis sekali sebagai fungsi, lalu dipasang ke elemen mana pun dengan use:namaFungsi. Bayangin rak di Toko Buku Kak Nadia. Ref itu seperti memegang langsung buku di rak, bukan cuma tahu judulnya. Directive itu seperti stiker aturan yang bisa ditempel di laci mana pun: "kalau ada yang menyentuh di luar laci, tutup lacinya".

🧪 Tebakan cepat: kotak cari ditulis <input ref={(el) => el.focus()} placeholder="Cari judul" />. Saat halaman tampil, kursornya masuk ke kotak itu atau gak? Jawabannya ada di bagian "Callback ref: dipanggil saat elemennya baru dibuat".

ref ke variabel: let kotak

import { onMount } from "solid-js"

function Cari() {
  let kotak
  console.log("badan:", kotak)

  onMount(() => {
    kotak.focus()
  })

  return <input ref={kotak} placeholder="Cari judul" />
}

Diuji di Solid 1.9.17:

Saat Isi kotak Sudah di halaman?
Badan komponen, console.log undefined belum ada elemen
return: <input> dibuat elemen <input> belum
onMount elemen <input> sudah, jadi focus() berhasil

Callback ref: dipanggil saat elemennya baru dibuat

ref juga menerima fungsi. Solid memanggilnya dengan elemennya, saat elemen itu baru dibuat, sebelum masuk ke halaman. Diuji di Solid 1.9.17: di dalam callback, el.isConnected bernilai false, dan el.focus() gak berbuat apa-apa, karena elemen di luar halaman gak bisa memegang fokus. Jadi kursornya gak masuk. Itu jawaban tebakannya.

Supaya berhasil, tunggu sampai elemennya terpasang:

// latihan pertama: tunggu onMount di dalam callback
<input ref={(el) => onMount(() => el.focus())} placeholder="Auto focus" />

// latihan kedua: tunda satu putaran, lalu fokus dan seleksi isinya
<input value="Select me!" ref={(el) => setTimeout(() => { el.focus(); el.select() }, 0)} />

use: directive, perilaku yang bisa dipakai ulang

Laci keranjang di toko harus tertutup kalau pembeli mengklik di luar laci. Perilaku itu ditulis sekali sebagai directive clickOutside:

import { createSignal, onCleanup, Show } from "solid-js"

function clickOutside(el, accessor) {
  const onClick = (e) => {
    if (!el.contains(e.target)) accessor()()
  }
  document.addEventListener("click", onClick)
  onCleanup(() => document.removeEventListener("click", onClick))
}

function App() {
  const [buka, setBuka] = createSignal(false)
  return (
    <div>
      <button onClick={() => setBuka(true)}>Keranjang (2)</button>
      <Show when={buka()}>
        <aside class="laci" use:clickOutside={() => setBuka(false)}>
          <p>Laskar Pelangi · Rp89.000</p>
          <p>Bumi Manusia · Rp120.000</p>
        </aside>
      </Show>
    </div>
  )
}

💡 TypeScript: daftarkan directive-nya

Di TypeScript, use:clickOutside baru dikenali setelah directive-nya didaftarkan:

declare module "solid-js" {
  namespace JSX {
    interface Directives {
      clickOutside: () => void
    }
  }
}

Tipe di sebelah nama directive adalah tipe nilainya, di sini fungsi tanpa argumen. Kalau clickOutside diimpor dari file lain dan cuma dipakai lewat use:, TypeScript bisa menganggap impornya gak terpakai lalu membuangnya. Pastikan namanya juga terpakai di kode biasa.

⚠️ Jebakan yang sering ketemu

TL;DR: let kotak plus <input ref={kotak} /> mengisi kotak dengan elemennya saat dibuat. Pakai di onMount(() => kotak.focus()), karena di badan komponen dia masih undefined. Callback ref ref={(el) => ...} dipanggil saat elemennya dibuat, sebelum masuk ke halaman, jadi el.focus() di sana gak berhasil: tunggu dengan onMount(() => el.focus()) atau setTimeout(() => { el.focus(); el.select() }, 0). Directive use:clickOutside={() => setBuka(false)} memanggil clickOutside(el, accessor), dengan accessor() sebagai nilainya, dan onCleanup di dalamnya melepas listener saat elemennya hilang.

Yang akan kamu pelajari