Lifecycle adalah perjalanan hidup sebuah komponen: dibuat, dipasang ke halaman (mount), diperbarui, lalu dilepas (destroy). Svelte menyediakan function untuk menitipkan kerja di tahap tertentu, terutama onMount dan onDestroy. Bayangin Bu Sri membuka warung. Rolling door dinaikkan dulu sampai warungnya kelihatan, baru dia menyalakan kompor dan menelepon pemasok untuk daftar bahan hari ini. Itu onMount. Waktu tutup, kompornya dimatikan sebelum pintu diturunkan. Itu onDestroy. Kalau lupa, gasnya terus menyala walaupun warungnya sudah tutup.
🧪 Tebakan cepat:
MenuHariIni.sveltememulaisetIntervaljam dionMount, tanpaonDestroy. Pembeli membuka menu, menutupnya, membuka lagi, menutup lagi, lalu membuka sekali lagi. Sekarang ada berapa interval yang jalan: 1 atau 3? Jawabannya ada di bagian "Interval yang lupa dihentikan".
Urutan hidup komponen
Ini yang terjadi saat App.svelte memasang <MenuHariIni />, dari Svelte 4 yang sebenarnya:
| Tahap | Kapan jalan | Di MenuHariIni |
|---|---|---|
<script> |
sekali, saat komponen dibuat | menu = [], jam diisi |
| render pertama | setelah script | <p>Memuat menu...</p> masuk halaman |
onMount |
sekali, setelah render pertama | interval jam dimulai, ambilMenu() dipanggil |
| update | tiap ada variabel yang berubah | jam berganti tiap detik, daftar menu muncul |
onDestroy |
sekali, sebelum komponen dilepas | interval dihentikan |
- Semua function lifecycle diimpor dari modul
"svelte":import { onMount, onDestroy } from "svelte". - Function itu harus dipanggil langsung di
<script>saat komponen dibuat, bukan di dalam handler klik. - Elemen DOM baru ada setelah render pertama. Variabel
bind:thismasihundefinedsaat<script>jalan, dan sudah berisi elemen dionMount.
onMount: mulai kerja setelah tampil
Ini MenuHariIni.svelte. Server-nya pura-pura: ambilMenu() menjawab setelah 800 ms.
<script>
import { onMount, onDestroy } from "svelte"
let menu = []
let jam = new Date().toLocaleTimeString("id-ID")
let timer
function ambilMenu() {
return new Promise((selesai) => {
setTimeout(() => selesai(["Nasi Goreng", "Mie Ayam", "Es Teh"]), 800)
})
}
onMount(async () => {
timer = setInterval(() => {
jam = new Date().toLocaleTimeString("id-ID")
}, 1000)
menu = await ambilMenu()
})
onDestroy(() => {
clearInterval(timer)
})
</script>
<p>Jam {jam}</p>
{#if menu.length === 0}
<p>Memuat menu...</p>
{:else}
<ul>
{#each menu as nama}
<li>{nama}</li>
{/each}
</ul>
{/if}
| Waktu | Yang tampil |
|---|---|
| 0 ms, render pertama | Jam 10.15.03, Memuat menu... |
| 800 ms | Jam 10.15.03, daftar Nasi Goreng, Mie Ayam, Es Teh |
| 1.000 ms | Jam 10.15.04, daftar yang sama |
onMount(callback)menjalankan callback-nya sekali, setelah komponen pertama kali tampil. Halaman gak perlu menunggu data:Memuat menu...sudah kelihatan dulu.menu = await ambilMenu()adalah assignment biasa, jadi tampilannya ikut diperbarui begitu datanya datang.- Kenapa gak langsung di
<script>? Di SvelteKit, komponen juga dirender di server, danonMountcuma jalan di browser. Ambil data dan mulai interval dionMount, supaya gak ikut jalan di server.
onDestroy: bereskan sebelum pergi
onDestroy(callback) jalan sekali, tepat sebelum komponen dilepas, misalnya saat {#if tampil} di App jadi false. Di sini tempat menghentikan apa pun yang kamu mulai: clearInterval, removeEventListener, atau berhenti berlangganan store.
onMount juga boleh mengembalikan function, dan Svelte menjalankannya sebagai cleanup saat komponen dilepas: onMount(() => { const t = setInterval(...); return () => clearInterval(t) }). Tapi itu cuma berlaku untuk callback biasa. Callback async selalu mengembalikan Promise, bukan function, jadi cleanup-nya diabaikan. Karena onMount di MenuHariIni async, interval-nya dihentikan di onDestroy.
Interval yang lupa dihentikan
Tanpa onDestroy, komponennya hilang dari halaman tapi interval-nya tetap jalan. Tiap buka-tutup meninggalkan satu:
| Kejadian | Komponen di halaman | Interval yang jalan |
|---|---|---|
| Buka | MenuHariIni #1 | 1 |
| Tutup | (gak ada) | 1, tanpa pemilik |
| Buka | MenuHariIni #2 | 2 |
| Tutup | (gak ada) | 2, tanpa pemilik |
| Buka | MenuHariIni #3 | 3 |
Jadi jawabannya 3 interval. Itu jawaban tebakannya. Dua di antaranya terus mengubah variabel milik komponen yang sudah gak ada. Ini disebut memory leak: kerja dan memori yang gak pernah dilepas. Dengan onDestroy(() => clearInterval(timer)), angkanya selalu 1 saat menu terbuka, dan 0 saat tertutup.
Latihan: pesan setelah mount
Kode awalnya punya let pesan = "Loading..." dan <p>{pesan}</p>. Jawabannya:
<script>
import { onMount } from "svelte"
let pesan = "Loading..."
onMount(() => {
pesan = "Komponen sudah dimount!"
})
</script>
<p>{pesan}</p>
Render pertama menampilkan Loading..., lalu onMount mengganti pesan, dan paragrafnya jadi Komponen sudah dimount!.
📌 beforeUpdate, afterUpdate, dan tick
Svelte 4 punya dua lagi. beforeUpdate jalan sebelum DOM diperbarui (termasuk sekali sebelum render pertama), dan afterUpdate jalan setelahnya. Urutan nyatanya saat komponen dibuat: <script>, beforeUpdate, onMount, lalu afterUpdate.
tick() adalah Promise yang selesai setelah pembaruan DOM berikutnya. Setelah porsi += 1, heading Porsi: {porsi} masih membaca Porsi: 1; setelah await tick(), baru Porsi: 2. Pakai tick kalau kodemu perlu membaca DOM yang baru.
Svelte 5
onMount, onDestroy, dan tick masih dipakai dengan cara yang sama di Svelte 5. beforeUpdate dan afterUpdate gak bisa dipakai di komponen runes: compiler menolak dengan beforeUpdate cannot be used in runes mode. Gantinya $effect.pre (sebelum DOM diperbarui) dan $effect (setelahnya). $effect juga bisa menggantikan pasangan onMount dan onDestroy untuk interval:
<script>
let jam = $state(new Date().toLocaleTimeString("id-ID"))
$effect(() => {
const timer = setInterval(() => {
jam = new Date().toLocaleTimeString("id-ID")
}, 1000)
return () => clearInterval(timer)
})
</script>
⚠️ Jebakan yang sering ketemu
- Interval tanpa
clearInterval. Tiap buka-tutup komponen meninggalkan satu interval yang terus jalan. Hentikan dionDestroy. - Cleanup dari
onMountyang async. Function yang dikembalikan callback async diabaikan, karena yang kembali adalah Promise. PakaionDestroy. - Memakai elemen DOM langsung di
<script>. Variabelbind:thismasihundefineddi sana. Pakai dionMountatau di handler. - Lupa mengimpor.
onMountgak tersedia otomatis; tulisimport { onMount } from "svelte". Penguji latihannya juga mencari panggilanonMount(di kodemu.
TL;DR: Komponen hidup dalam urutan: <script> jalan, render pertama, onMount, update tiap ada perubahan, lalu onDestroy sebelum dilepas. Semua function lifecycle diimpor dari "svelte". onMount jalan sekali setelah komponen tampil dan cuma di browser, jadi tempat mengambil data (menu = await ambilMenu()) dan memulai interval. onDestroy tempat bersih-bersih, misalnya clearInterval(timer); tanpa itu, tiap buka-tutup meninggalkan interval yang terus jalan. onMount boleh mengembalikan cleanup, tapi gak kalau callback-nya async. tick() menunggu DOM selesai diperbarui. Di Svelte 5, beforeUpdate dan afterUpdate diganti $effect.pre dan $effect. Latihannya: onMount(() => { pesan = "Komponen sudah dimount!" }), sehingga <p> berubah dari Loading... jadi Komponen sudah dimount!.