4. L'état

2. Réactivité des objects et des tableaux

Toujours à faire les intéressants ceux-là...

Vous pouvez bien sûr utiliser $state pour déclarer un objet ({...}) ou un tableau ([...]).

Réactivité profonde

Avec les objets et les tableaux, vous pouvez notamment muter variable déclarée avec $state et quand même bénéficier de la réactivité. C'est ce qu'on appelle la réactivité profonde.

Par exemple, vous pouvez utiliser delete pour supprimer une clé d'un objet, ou encore .push() (ou autre) pour augmenter un tableau, et les mises à jour se feront normalement, comme attendu :

<script>
	let obj = $state({ a: 1, b: 2 });
	let arr = $state([1, 2]);

	function update() {
		delete obj.a;
		arr.push(3);
	}
</script>

<button onclick={update}> Update </button>

{#each Object.keys(obj) as k}
	<!-- la clé 'a' est bien supprimée au clic sur le bouton  -->
	<p>{k}</p>
{/each}

{#each arr as nb}
	<!-- le nombre d'éléments affichés augmente bien au clic sur le bouton `-->
	<p>{nb}</p>
{/each}

En Svelte 4, la réactivité fonctionnait complètement différemment, et nécessitait l'usage d'une assignation (truc = machin) pour fonctionner. Écrire quelque chose comme delete obj.a ou arr.push() n'aurait eu aucun effet. Ajouter un élément à un tableau nécessitait donc d'écrire quelque chose comme tableau = [...tableau, element].

Réactivité fine

En Svelte 4, il était possible de muter un objet (ou un tableau), à condition d'utiliser une assignation. Ceci est bien sûr toujours possible avec Svelte 5 :

<script>
	let obj = $state({ a: 1, b: 2 });

	function update() {
		obj.a = 2;
	}
</script>

<button onclick={update}> Update </button>

{#each Object.entries(obj) as [key, value]} <p>{key} : {value}</p> {/each}

Néanmoins, avec la rune $state, la réactivité de Svelte 5 est "fine" (fine-grained). Cela signifie que lorsqu'on mute une propriété d'un objet, cette propriété uniquement est "invalidée", et non tout l'objet. En d'autres termes, uniquement les choses dont dépendent cette propriété seront mises à jour, et non l'entièreté des dépendances de l'objet.

Cela permet d'optimiser la performance générale de votre application en évitant des calculs inutiles.

Voici un exemple simple du problème avec la réactivité de Svelte 4 :

<!-- Svelte 4 -->
<script>
	let todos = [
		{ done: false, text: 'Dormir' },
		{ done: false, text: 'Manger' }
	];

	function getLength(arr) {
		console.log('Calcul'); // inutilement ré-exécuté lorsqu'on met à jour les textes des tâches
		return arr.length;
	}
</script>

{#each todos as todo}
	<div><input bind:value={todo.text} /> <input type="checkbox" bind:checked={todo.done} /></div>
{/each}

<p>Nombre de tâches : {getLength(todos)}</p>

Et son équivalent en Svelte 5 :

<!-- Svelte 5 -->
<script>
	let todos = $state([
		{ done: false, text: 'Dormir' },
		{ done: false, text: 'Manger' }
	]);

	function getLength(arr) {
		console.log('Calcul'); // n'est jamais recalculé si l'on change juste les textes
		return arr.length;
	}
</script>

{#each todos as todo}
	<div><input bind:value={todo.text} /> <input type="checkbox" bind:checked={todo.done} /></div>
{/each}

<p>Nombre de tâches : {getLength(todos)}</p>

Vous trouverez une illustration plus complète de l'importance de cette différence ici (Svelte 4) versus .

À vous !

Dans la page d'accueil

  • Créer un état foundSpecies représenant un tableau des id des espèces de Pokémons déjà découverts. Initialiser foundSpecies à [].

  • Afficher quelque part dans la page la taille de ce tableau.

  • Au clic sur un des 3 Pokémons de départ, ajouter son id à foundSpecies si vous ne l'avez pas déjà trouvé.


Plus de détails sur ce chapitre