Portals dan Dynamic — SolidJS

Portal merender elemen di luar parent DOM tree. Dynamic merender komponen secara dinamis. Portal import { Portal } from "solid-js/web" function Modal(props) {…

<Portal> merender sepotong JSX di tempat lain di DOM, biasanya langsung di document.body, padahal di kodemu dia tetap anak komponen yang menulisnya. <Dynamic> merender tag atau komponen yang baru dipilih saat aplikasinya jalan. Bayangin Kak Nadia menulis pengumuman promo di meja kasir, tapi menempelnya di papan besar dekat pintu masuk, supaya gak tertutup rak mana pun. Yang menulis tetap kasir, yang menempel tetap di papan. Dynamic itu seperti info buku yang sama dicetak jadi poster besar atau label kecil, tergantung pilihan saat itu.

🧪 Tebakan cepat: modal detail buku dirender lewat <Portal> ke document.body. <div class="kartu"> yang membungkus Portal-nya punya onClick. Kamu mengklik teks di dalam modal. onClick di kartu jalan atau gak? Jawabannya ada di bagian "Event tetap ikut pohon komponen".

Masalahnya: kartu yang memotong modal

Kartu buku di toko memakai overflow: hidden, supaya sampulnya ikut sudut kartu yang membulat. Modal detail yang ditulis di dalam kartu ikut terpotong di tepi kartu itu, dan z-index setinggi apa pun gak menolong, karena dia terkurung di dalam kartu. Modal, toast, tooltip, dan dropdown butuh keluar dari kotak induknya. Itu tugas Portal.

Portal dan mount

import { createSignal, Show } from "solid-js"
import { Portal } from "solid-js/web"

function KartuBuku(props) {
  const [buka, setBuka] = createSignal(false)
  return (
    <div class="kartu" onClick={() => console.log("kartu menerima klik")}>
      <h3>{props.judul}</h3>
      <button onClick={() => setBuka(true)}>Lihat detail</button>
      <Show when={buka()}>
        <Portal>
          <div class="modal">
            <p>{props.judul} · Rp89.000 · stok 4</p>
            <button onClick={() => setBuka(false)}>Tutup</button>
          </div>
        </Portal>
      </Show>
    </div>
  )
}

Diuji di Solid 1.9.17, sesudah Lihat detail diklik, DOM-nya:

<body>
  <div id="root">
    <div class="kartu"><h3>Laskar Pelangi</h3><button>Lihat detail</button></div>
  </div>
  <div><div class="modal"><p>Laskar Pelangi · Rp89.000 · stok 4</p><button>Tutup</button></div></div>
</body>

Event tetap ikut pohon komponen

Di DOM, modal itu anak body. Di kodemu, dia anak KartuBuku. Solid mengikuti yang kedua. Diuji di Solid 1.9.17: klik teks di dalam modal menjalankan onClick di .kartu dan mencetak kartu menerima klik. Itu jawaban tebakannya.

Sebabnya: onClick di Solid didelegasikan, cukup satu listener di document yang menelusuri ke atas dari elemen yang diklik. Sampai di <div> buatan Portal, penelusurannya melompat ke tempat <Portal> ditulis, lalu lanjut ke .kartu. Listener asli lewat on:click atau addEventListener di .kartu gak mendengarnya, karena di DOM jalurnya modal, <div>, lalu body. Context juga tetap sampai: useContext di dalam Portal membaca Provider di sekitar <Portal>.

Dynamic: tag atau komponen yang dipilih saat jalan

Bentuk latihan pelajaran ini:

import { createSignal } from "solid-js"
import { Dynamic } from "solid-js/web"

function App() {
  const [tag, setTag] = createSignal("h1")

  return (
    <div>
      <Dynamic component={tag()}>Heading Dinamis</Dynamic>
      <button onClick={() => setTag(t => t === "h1" ? "h2" : "h1")}>
        Toggle Tag
      </button>
    </div>
  )
}
Kejadian tag() Yang tampil
Awal "h1" <h1>Heading Dinamis</h1>
Klik sekali "h2" <h2>Heading Dinamis</h2>
Klik lagi "h1" <h1>Heading Dinamis</h1>

component juga menerima komponen. Toko bisa menampilkan buku sebagai kartu grid atau baris daftar:

const tampilan = { grid: KartuGrid, baris: KartuBaris }

<Dynamic component={tampilan[mode()]} judul="Laskar Pelangi" stok={stok()} />

Prop lain, judul dan stok, diteruskan ke komponen yang terpilih dan tetap reaktif. Diuji di Solid 1.9.17: mengganti mode membuat komponen yang baru, mengubah stok cuma mengganti angkanya tanpa membuat ulang komponen.

⚠️ Jebakan yang sering ketemu

TL;DR: <Portal> dari "solid-js/web" memasang isinya di document.body (atau di elemen pilihan lewat mount={...}), keluar dari kotak induk yang memotongnya, tapi tetap bagian dari pohon komponen: onClick di komponen induk, context, dan Show di sekitarnya tetap berlaku. <Dynamic component={tag()}> merender tag atau komponen yang dipilih saat jalan, meneruskan prop lain, dan membuat ulang isinya cuma saat komponennya berganti. Latihannya: Heading Dinamis jadi h2 sesudah satu klik dan kembali h1 sesudah dua klik.

Yang akan kamu pelajari