$effect en de levenscyclus in Svelte 5: wanneer moet je het gebruiken (en wanneer niet)
Als er één Rune of Svelte 5 is die meer verkeerd wordt begrepen en misbruikt dan de andere, dan is het wel $effect.
Afkomstig van React, waar useEffect en vaak de hamer voor elke spijker, en natuurlijk
probeer dat patroon in Svelte te repliceren. Maar $effect heeft een andere filosofie, a
een beperkter toepassingsgebied, en het negeren ervan leidt direct tot de ergste antipatronen van het raamwerk.
In deze handleiding wordt precies gedefinieerd wat het doet $effect, net als het volgmechanisme
automatisch werkt intern, wat is de gouden regel om te beslissen of je het wel of niet wilt gebruiken, en het biedt
concrete voorbeelden van de belangrijkste legitieme gebruiksscenario's: analyses, WebSockets, integratie met bibliotheken
derden, en synchroniseer ermee localStorage.
De gouden regel van $effect
Voordat je gaat schrijven $effect, vraag jezelf af: "Bereken ik iets op basis van de bestaande toestand?"
Als het antwoord ja is, gebruik dan $derived. $effect en voor echte bijwerkingen:
bewerkingen die interageren met de wereld buiten de gebruikersinterface (directe DOM, netwerk, opslag, timer,
bibliotheken van derden).
Hoe $effect werkt: automatisch volgen
$effect voert een functie uit nadat de DOM is bijgewerkt. De functie
fundamenteel en zo volgt automatisch elke $state en $derived die tijdens het lezen wordt gelezen
de uitvoering ervan en wordt opnieuw uitgevoerd telkens wanneer een van deze afhankelijkheden verandert.
Het is niet nodig om afhankelijkheden expliciet te declareren.
<script lang="ts">
let count = $state(0);
let name = $state('Federico');
// Questo effect dipende da ENTRAMBI count e name
// perche li legge entrambi durante l'esecuzione
$effect(() => {
console.log(`${name} ha un contatore di ${count}`);
// Si ri-esegue quando count O name cambiano
});
// Questo effect dipende SOLO da count
$effect(() => {
document.title = `Contatore: ${count}`;
// Si ri-esegue SOLO quando count cambia
});
</script>
Het volgmechanisme gebruikt hetzelfde proxysysteem als $state: tijdens uitvoering
van $effect, wordt elke toegang tot een reactieve waarde geregistreerd. Wanneer enige
Als deze waarden veranderen, wordt het effect gemarkeerd als "vies" en gepland om opnieuw te worden afgespeeld.
De opruimfunctie
$effect kan een opschoonfunctie retourneren die vóór de
volgende heruitvoering van het effect en wanneer het onderdeel wordt gedemonteerd. Het is essentieel voor
vermijd geheugenlekken met timers, abonnementen en gebeurtenislisteners:
<script lang="ts">
let wsUrl = $state('wss://api.example.com/stream');
let messages = $state<string[]>([]);
$effect(() => {
// Effect traccia wsUrl: si ri-esegue se l'URL cambia
const ws = new WebSocket(wsUrl);
ws.addEventListener('message', (event) => {
messages.push(event.data);
});
ws.addEventListener('error', (error) => {
console.error('WebSocket error:', error);
});
// Cleanup: chiude la connessione prima della prossima esecuzione
// e quando il componente viene smontato
return () => {
ws.close();
console.log('WebSocket chiuso');
};
});
</script>
Se wsUrl wijzigingen, Svelte 5 ruimt op (sluit de oude WS-verbinding),
voert het effect vervolgens opnieuw uit (opent een nieuwe verbinding met de nieuwe URL). En een schoon patroon
waarvoor geen handmatig levenscyclusbeheer vereist is.
Levenscyclus: $effect versus onMount versus onDestroy
Svelte 5 onderhoudt de levenscyclusfuncties van Svelte 4 (onMount, onDestroy,
enz.) voor achterwaartse compatibiliteit, maar Runen bieden een meer uniforme aanpak:
<script lang="ts">
import { onMount, onDestroy } from 'svelte'; // Svelte 4 style
let data = $state(null);
// --- Svelte 4 pattern ---
onMount(async () => {
data = await fetchData();
});
// --- Svelte 5 equivalente con $effect ---
$effect(() => {
// $effect.root per effects che non dipendono da stato
// e si eseguono una sola volta (equivalente di onMount)
fetchData().then(result => {
data = result;
});
return () => {
// cleanup equivalente a onDestroy
};
});
// --- Svelte 5 per operazioni truly una-tantum ---
// Preferisci il blocco di codice nel componente
// o usa untrack() per evitare tracking accidentale
import { untrack } from 'svelte';
$effect(() => {
// untrack impedisce il tracking di initialValue
const initial = untrack(() => data);
console.log('Valore iniziale (non tracciato):', initial);
// count E tracciato perche e fuori da untrack
console.log('Count corrente:', count);
});
</script>
Legitieme gebruiksscenario's van $effect
Hier zijn de belangrijkste scenario's waarin $effect en de juiste keuze:
1. Synchronisatie met localStorage
<script lang="ts">
// Leggi il valore iniziale da localStorage (fuori da $effect)
let theme = $state<'light' | 'dark'>(
(localStorage.getItem('theme') as 'light' | 'dark') ?? 'light'
);
// Sincronizza le modifiche a localStorage
$effect(() => {
localStorage.setItem('theme', theme);
document.documentElement.setAttribute('data-theme', theme);
});
</script>
<button onclick={() => theme = theme === 'light' ? 'dark' : 'light'}>
Toggle theme
</button>
2. Analyse en tracking
<script lang="ts">
const { pageId, userId }: { pageId: string; userId?: string } = $props();
// Traccia la visualizzazione di pagina quando pageId cambia
$effect(() => {
// Sia pageId che userId sono tracciati
analytics.track('page_view', {
page: pageId,
user: userId ?? 'anonymous',
timestamp: new Date().toISOString()
});
});
</script>
3. Integratie met DOM-bibliotheken van derden
<script lang="ts">
import Chart from 'chart.js/auto';
let canvasEl: HTMLCanvasElement;
let chartData = $state({ labels: [], datasets: [] });
let chartInstance: Chart | null = null;
$effect(() => {
// Prima esecuzione: crea il chart
if (!chartInstance) {
chartInstance = new Chart(canvasEl, {
type: 'bar',
data: chartData
});
} else {
// Ri-esecuzioni: aggiorna i dati
chartInstance.data = chartData;
chartInstance.update();
}
return () => {
// Cleanup: distruggi il chart
chartInstance?.destroy();
chartInstance = null;
};
});
</script>
<canvas bind:this={canvasEl}></canvas>
4. Intersection Observer (lui laden, animaties bij scrollen)
<script lang="ts">
let element: HTMLElement;
let isVisible = $state(false);
let hasBeenVisible = $state(false);
$effect(() => {
const observer = new IntersectionObserver(
(entries) => {
isVisible = entries[0].isIntersecting;
if (isVisible) hasBeenVisible = true;
},
{ threshold: 0.1 }
);
observer.observe(element);
return () => observer.disconnect();
});
</script>
<div
bind:this={element}
class:visible={isVisible}
class:animated={hasBeenVisible}
>
Contenuto che appare con animazione
</div>
Antipatroon: gebruik $effect niet voor berekeningen
Het meest voorkomende antipatroon met $effect en gebruik het om de status bij te werken die van iets anders is afgeleid
staat. Dit zorgt voor inefficiënte en potentieel oneindige updatecycli:
<script lang="ts">
let items = $state([1, 2, 3, 4, 5]);
let total = $state(0); // SBAGLIATO: non dovrebbe essere $state
// ANTI-PATTERN: usare $effect per calcolare un valore derivato
$effect(() => {
total = items.reduce((s, i) => s + i, 0);
// Questo crea: items cambia -> effect corre -> total cambia ->
// potenziali altri effects si ri-eseguono inutilmente
});
// CORRETTO: usare $derived
const totalCorrect = $derived(items.reduce((s, i) => s + i, 0));
</script>
<script lang="ts">
let searchQuery = $state('');
let filteredUsers = $state([]); // SBAGLIATO
// ANTI-PATTERN: filtrare con $effect
$effect(() => {
filteredUsers = users.filter(u =>
u.name.toLowerCase().includes(searchQuery.toLowerCase())
);
});
// CORRETTO: $derived
const filteredUsersCorrect = $derived(
users.filter(u =>
u.name.toLowerCase().includes(searchQuery.toLowerCase())
)
);
</script>
$effect en oneindige lussen
Een klassieke fout en bewerking van binnen $effect een van de $staten dat het effect
hetzelfde nummer, waardoor een oneindige lus ontstaat:
// CICLO INFINITO: count tracciato, count modificato
$effect(() => {
console.log(count); // traccia count
count++; // modifica count -> ri-esegue l'effect
});
Svelte 5 detecteert lussen en genereert een ontwikkelingsfout. Als u $state van een $effect moet wijzigen,
gebruik untrack() om te lezen zonder over te trekken, of om het ontwerp te heroverwegen.
$effect.root en $effect.pre
Svelte 5 biedt twee gespecialiseerde varianten van $effect:
<script lang="ts">
// $effect.pre: esegue PRIMA dell'aggiornamento del DOM
// Utile per leggere il DOM prima che venga modificato
// (es: salvare la posizione di scroll prima di un aggiornamento)
$effect.pre(() => {
const scrollPos = window.scrollY;
return () => {
// Ripristina scroll dopo l'aggiornamento del DOM
window.scrollTo(0, scrollPos);
};
});
// $effect.root: crea un effetto che non e legato al ciclo di vita
// del componente, utile fuori dai componenti
const cleanup = $effect.root(() => {
$effect(() => {
// Effect che persiste oltre la distruzione del componente
// (utile per singleton e stores globali)
});
return () => { /* cleanup manuale */ };
});
</script>
Code testen met $effect
Onderdelen testen met $effect vereist aandacht: de effecten zijn gepland
asynchroon, dus de tests moeten wachten tot de updatecyclus is voltooid:
// test/MyComponent.test.ts
import { render } from '@testing-library/svelte';
import { tick } from 'svelte';
import MyComponent from './MyComponent.svelte';
test('$effect sincronizza con localStorage', async () => {
const { getByRole } = render(MyComponent);
const button = getByRole('button', { name: 'Toggle theme' });
button.click();
// Aspetta che il ciclo di aggiornamento Svelte completi
await tick();
expect(localStorage.getItem('theme')).toBe('dark');
});
Beslissing: $effect of $afgeleid?
Het vereenvoudigde beslissingsdiagram:
- "Moet ik een waarde berekenen op basis van de bestaande status?" → gebruiken
$derived - “Moet ik een operatie ondergaan met bijwerkingen?” → gebruiken
$effect - "Moet ik de DOM rechtstreeks bijwerken?" → gebruiken
$effect(of beter gezegd, een actie Svelte) - "Moet ik een API-aanroep doen als de status verandert?" → gebruiken
$effect - "Moet ik getransformeerde gegevens in de gebruikersinterface weergeven?" → gebruiken
$derived
Conclusies en volgende stappen
$effect en krachtig maar gespecialiseerd: het wordt gebruikt om de responsieve gebruikersinterface van Svelte te verbinden met de
externe wereld - DOM, netwerk, opslag, bibliotheken van derden. Gebruik het voor berekeningen die erbij horen
een $derived introduceert onnodige complexiteit en potentiële bugs. Het patroon om te onthouden:
als het een waarde produceert die in de gebruikersinterface moet worden weergegeven, bijv $derived; als het ergens mee in wisselwerking staat
buiten de gebruikersinterface, bijv $effect.
Het volgende artikel gaat over SvelteKit in detail: hoe belastingen werken functies voor het ophalen van gegevens op de server, hoe SSR-streaming TTFB vermindert en hoe formulieren acties verwerken mutaties zonder JavaScript aan de clientzijde.
Serie: Svelte 5 en frontend-compilergestuurd
- Artikel 1: Compilergestuurde aanpak en mentaal model
- Artikel 2: $state en $derived – Universele reactiviteit met runen
- Artikel 3 (dit): $effect en de levenscyclus – wanneer moet u het gebruiken (en wanneer niet)
- Artikel 4: SvelteKit SSR, streaming en laadfuncties
- Artikel 5: Overgangen en animaties in Svelte 5
- Artikel 6: Toegankelijkheid in Svelte: compilerwaarschuwingen en best practices
- Artikel 7: Mondiaal staatsbeheer: context, runen en winkels
- Artikel 8: Migreren van Svelte 4 naar Svelte 5 — Praktische gids
- Artikel 9: Testen in Svelte 5: Vitest, Testbibliotheek en Toneelschrijver







