Eine der häufigsten Fragen rund um Actions: „Was passiert, wenn sich der Parameter zur Laufzeit ändert?" Die Antwort ist nicht ganz selbsterklärend — denn die Action-Funktion selbst läuft nur einmal beim Mount. Dieser Artikel zeigt, wie du trotzdem auf Änderungen reagierst, was der Unterschied zwischen der klassischen update-Methode und der modernen $effect-Variante ist und welche Stolperfalle vielen Entwicklern unterläuft.

Was passiert, wenn sich der Parameter ändert?

Stell dir vor, du hast eine Tooltip-Action, deren Text sich ändern können soll:

svelte Reaktiver Parameter
<script>
    import { tooltip } from '$lib/actions/tooltip.svelte';
    let label = $state('Hallo');
</script>

<button use:tooltip={{ text: label }}>Hover</button>

<input bind:value={label} />

Wenn der Nutzer im Eingabefeld tippt, ändert sich label. Die Frage ist: Bekommt die Tooltip-Action diese Änderung mit?

Die ehrliche Antwort: Es kommt darauf an, wie die Action geschrieben ist. Es gibt zwei Wege.

Weg 1 – Klassische update-Methode

In der älteren Schreibweise gibst du aus der Action ein Objekt mit einer update-Methode zurück. Svelte ruft diese Methode jedes Mal auf, wenn sich der use:-Parameter ändert.

ts Klassischer Tooltip
export function tooltip(node: HTMLElement, params: { text: string }) {
    let currentText = params.text;

    const handler = () => {
        console.log('Tooltip:', currentText);
    };

    node.addEventListener('mouseenter', handler);

    return {
        update(newParams: { text: string }) {
            currentText = newParams.text;
        },
        destroy() {
            node.removeEventListener('mouseenter', handler);
        },
    };
}

Verhalten:

  • Setup läuft einmal beim Mount mit params.
  • Bei jeder Änderung von label ruft Svelte update({ text: neuerWert }) auf.
  • Beim Unmount ruft Svelte destroy().

Diese Schreibweise ist solide und funktioniert seit Svelte 3.

Weg 2 – Moderne Variante mit $effect

In der Runes-Welt schreibt man dasselbe Verhalten kompakter — und mit klarer reaktiver Semantik:

ts Tooltip mit $effect
import type { Action } from 'svelte/action';

export const tooltip: Action<HTMLElement, { text: string }> = (node, params) => {
    $effect(() => {
        const text = params.text; // Tracking
        const handler = () => console.log('Tooltip:', text);
        node.addEventListener('mouseenter', handler);
        return () => node.removeEventListener('mouseenter', handler);
    });
};

Was hier passiert:

  1. $effect läuft beim ersten Mount mit dem aktuellen Parameter.
  2. Sobald params.text sich ändert, wird der Cleanup ausgeführt (Listener entfernt) und der Effect läuft neu.
  3. Beim Unmount läuft der Cleanup ein letztes Mal.

Vorteil: Kein separates update-Konstrukt. Reaktivität funktioniert hier genauso wie überall sonst in Runes.

Die wichtigste Stolperfalle: nicht-reaktive Parameter

Achtung — die Action wird immer nur einmal aufgerufen. Wenn du den Parameter nur außerhalb eines $effect liest, bekommst du keine Reaktivität:

ts − Funktioniert nicht reaktiv
export function tooltip(node: HTMLElement, params: { text: string }) {
    // params.text wird einmalig beim Mount gelesen
    const handler = () => console.log('Tooltip:', params.text);
    node.addEventListener('mouseenter', handler);
}

Das sieht erst mal richtig aus — aber: Wenn params.text sich später ändert, weiß der Handler nichts davon. Er hat den Wert von damals geschlossen.

Wichtig: Das Parameter-Objekt selbst ist in Svelte 5 reaktiv. Wenn du es innerhalb von $effect liest, wird die Reaktivität verfolgt:

ts + Reaktiv im effect
export function tooltip(node: HTMLElement, params: { text: string }) {
    $effect(() => {
        const text = params.text; // <-- hier wird getrackt
        const handler = () => console.log('Tooltip:', text);
        node.addEventListener('mouseenter', handler);
        return () => node.removeEventListener('mouseenter', handler);
    });
}

Zugriff auf params.text innerhalb des $effect löst beim Wechsel ein erneutes Auführen aus.

Mehrere reaktive Parameter

Wenn dein Action-Parameter mehrere Felder hat, kannst du sie entweder gemeinsam oder einzeln tracken — je nachdem, was du brauchst.

ts Granular tracken
export function tooltip(node: HTMLElement, params: { text: string; placement: 'top' | 'bottom' }) {
    // Effect, der nur auf text reagiert
    $effect(() => {
        node.dataset.tooltipText = params.text;
    });

    // Effect, der nur auf placement reagiert
    $effect(() => {
        node.dataset.tooltipPlacement = params.placement;
    });
}

Jeder $effect verfolgt automatisch nur die darin gelesenen Werte. Eine Änderung an text löst nur den ersten Effect aus, eine Änderung an placement nur den zweiten.

Wenn der Parameter teure Operationen auslöst

Manchmal soll bei Parameter-Wechsel nur ein Wert aktualisiert werden, ohne den ganzen Setup neu zu machen. Beispiel: Eine Library-Instanz, die teuer zu erzeugen ist, aber eine günstige setOption()-Methode anbietet.

ts Setup einmal, Updates billig
export function chart(node: HTMLElement, params: { data: number[] }) {
    // Setup läuft einmal, ohne Tracking
    const instance = new ChartLib(node);

    $effect(() => {
        // Nur Daten reaktiv aktualisieren
        instance.setData(params.data);
    });

    // Unmount-Cleanup
    $effect(() => {
        return () => instance.destroy();
    });
}

Hier wird die Library-Instanz nur einmal erzeugt. Bei jeder Datenänderung wird nur die günstige setData-Methode aufgerufen — kein vollständiges Re-Setup.

Vergleich: update vs. $effect

Aspektupdate-Methode (klassisch)$effect (modern)
SchreibweiseObjekt mit update/destroyEine Funktion mit $effect
Reaktivität auf einzelne FelderManuell vergleichenAutomatisch granular
CleanupIm destroyRückgabefunktion in $effect
Funktioniert in .tsJaNur in .svelte.ts/.svelte
Lesbarkeit bei mehreren EffectsSchwierig (alles in update)Ein $effect pro Verantwortung
Performance bei häufigen UpdatesManuelle Optimierung möglichGranular automatisch
Empfehlung Svelte 5Bestandscode behaltenNeuer Code

Besonderheiten

Parameter außerhalb von $effect lesen.

Klassischer Fehler: Du liest params.text direkt im Action-Body, nicht im $effect. Reaktivität funktioniert dann nicht.

Closure schließt veraltete Parameter ein.

Ein Listener, der einmalig beim Mount registriert wird, schließt den damaligen Parameter-Wert ein. Wenn der Parameter sich ändert, sieht der Listener weiterhin den alten Wert. Lösung: Listener im $effect registrieren, mit Cleanup.

Cleanup vergessen.

Wer in $effect Listener oder Observer registriert, ohne Cleanup, hat bei jedem Re-Run Memory-Leaks.

update-Methode mit komplexer Logik.

Wenn update zu groß wird, lohnt der Wechsel auf $effect-basierte Form — automatisches Tracking ist meist klarer.

Action ohne Parameter, aber Aufruf mit {}.

use:meineAction ohne Parameter ist okay. use:meineAction={{}} ist auch okay. Aber wenn die Action keinen zweiten Parameter erwartet, hat das Übergeben keinen Effekt — und kann verwirrend sein.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Actions

Zur Übersicht