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 :
- app.svelte : Un composant parent avec des boutons pour sélectionner différents noms
- 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éactivechanged
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 :
- La valeur de
name
est mise à jour - La valeur dérivée
changed
est recalculée - 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 etoriginal
ne serait jamais mise à jour - Avec
{#key}
, Svelte détruit et recrée le composantInputName
, ce qui entraîne une nouvelle exécution du script et une réinitialisation deoriginal
Conclusion
- Être conscient de l’initialisation unique : Les variables assignées directement dans le bloc script ne sont initialisées qu’une seule fois.
- Utiliser des déclarations réactives : Exploiter
$derived
pour les valeurs qui doivent réagir aux changements de props. - Comprendre quand utiliser
{#key}
: L’utiliser lorsque vous devez réinitialiser complètement l’état d’un composant.