Comprendre le Cycle de Vie des Composants et la Réactivité dans Svelte

samedi 1 mars 2025 · 4 minutes · 661 mots

Le système de composants de Svelte propose une approche spécifique de la réactivité et de l’initialisation des composants. Cet article explore une idée reçue concernant le fonctionnement des props, des variables et de l’initialisation des composants dans Svelte.

L’Idée Reçue

Les développeurs débutant avec Svelte pourraient supposer que lorsqu’une prop change, l’intégralité du script du composant s’exécute à nouveau, comme si le composant était une fonction et la prop un paramètre : si la prop change, la fonction donnera un autre résultat, n’est-ce pas ? Ce n’est malheureusement pas ainsi que Svelte fonctionne.

Analyse de l’Exemple

L’exemple démontre ce concept avec deux composants :

  1. app.svelte : Un composant parent avec des boutons pour sélectionner différents noms
  2. name.svelte : Un composant enfant qui reçoit une prop de nom et qui indique si il y a des modifications

Le Composant Parent (app.svelte)

<script>
    // Ceci montre que changer la prop ne réexécute pas
    // le script du composant.
	
    import InputName from "./name.svelte";
    let names = ["John", "Mila", "Ali"];	
    let selected = $state();
</script>

<!-- Ce sélecteur déclenche le changement de prop
    dans le composant enfant -->
{#each names as name}
    <button onclick={() => selected=name}>{name}</button>
{/each}

<!-- Cette section appelle le composant enfant
    avec la prop -->
{#if selected}
    <InputName name={selected}/>
{/if}

Le composant parent :

  • Maintient une liste de noms et un état de nom sélectionné
  • Affiche des boutons pour chaque nom qui mettent à jour la variable selected
  • Affiche également le composant InputName avec le nom sélectionné

Le Composant Enfant (name.svelte)

<script>
    // Composant simple avec indication de modification
	
    let { name } = $props();
	
    // exécuté une seule fois au montage du composant
    let original = name;

    let changed = $derived(original != name);
</script>

Nom : <input type="text" bind:value={name}>

{#if changed}
    modifié
{/if}

Le composant enfant :

  • Reçoit une prop name en utilisant $props()
  • Stocke la valeur initiale dans une variable original
  • Utilise $derived pour créer une variable réactive changed qui compare les valeurs actuelles et originales
  • Affiche “modifié” lorsque la valeur d’entrée diffère de l’original

Le Problème

Lorsque nous cliquons sur un nom et commençons à mettre à jour le champ, l’indicateur ‘modifié’ s’affiche. Cela semble fonctionner correctement.

Cependant, lorsque nous cliquons sur un autre nom, l’indicateur reste visible. Nous pouvons également déclencher ce problème en cliquant sur le premier nom sans le modifier, puis en cliquant sur un deuxième nom : l’indicateur ‘modifié’ apparaît alors que nous n’avons rien mis à jour.

L’élément clé est que le bloc script du composant ne s’exécute qu’une seule fois lors du montage initial du composant. Lorsque les props changent, Svelte met à jour les dépendances réactives sans réexécuter l’intégralité du script.

Cela signifie que let original = name; n’est exécuté qu’une seule fois lors de l’initialisation du composant. Lorsque la prop name change par la suite :

  1. La valeur de name est mise à jour
  2. La valeur dérivée changed est recalculée
  3. Mais original conserve toujours la première valeur qu’elle a reçue

La Solution

Pour forcer le composant enfant à réexécuter son script lorsque la prop name change, nous utilisons un bloc {#key} dans le composant parent.

Le bloc {#key variable} se déclenche lors des mises à jour de variable en détruisant et en recréant les composants qu’il entoure.

Pour notre exemple, nous devons modifier notre code comme ceci :

<!-- Cette section appelle le composant enfant avec la prop -->
{#if selected}
    {#key selected}
        <InputName name={selected}/>
    {/key}
{/if}

Lorsque selected change :

  • Sans {#key}, la même instance de composant resterait et original ne serait jamais mise à jour
  • Avec {#key}, Svelte détruit et recrée le composant InputName, ce qui entraîne une nouvelle exécution du script et une réinitialisation de original

Conclusion

  1. Être conscient de l’initialisation unique : Les variables assignées directement dans le bloc script ne sont initialisées qu’une seule fois.
  2. Utiliser des déclarations réactives : Exploiter $derived pour les valeurs qui doivent réagir aux changements de props.
  3. Comprendre quand utiliser {#key} : L’utiliser lorsque vous devez réinitialiser complètement l’état d’un composant.
Svelte