Faire évoluer l’état par des actions
Objectif : Transformer une action utilisateur en mise à jour explicite de l’état.
Quand vous ajoutez une tâche, la liste change. Si chaque interaction modifie l’état à sa manière, il devient difficile de comprendre d’où vient une mise à jour ou de vérifier son résultat. Vous allez formaliser les changements avec des actions et une fonction réductrice, sans encore connecter ces éléments à l’interface.
Décrire une intention avec une action
Une action est un objet qui décrit un événement à prendre en compte. Elle indique un type, puis les informations nécessaires à la transition. Par exemple, l’ajout d’une tâche a besoin d’un identifiant et d’un libellé ; son achèvement a besoin de l’identifiant de la tâche concernée.
import { nettoyerLibelle } from "./etat.js";
export function reduireEtat(etat, action) {
switch (action.type) {
case "tache/ajouter": {
const libelle = nettoyerLibelle(action.libelle);
if (libelle === "" || etat.taches.some((tache) => tache.id === action.id)) {
return etat;
}
const nouvelleTache = {
id: action.id,
libelle,
terminee: false
};
return {
...etat,
taches: [...etat.taches, nouvelleTache]
};
}
case "tache/basculer": {
const existe = etat.taches.some((tache) => tache.id === action.id);
if (!existe) return etat;
return {
...etat,
taches: etat.taches.map((tache) =>
tache.id === action.id
? { ...tache, terminee: !tache.terminee }
: tache
)
};
}
case "tache/supprimer": {
const taches = etat.taches.filter((tache) => tache.id !== action.id);
if (taches.length === etat.taches.length) return etat;
return { ...etat, taches };
}
default:
return etat;
}
}La fonction réductrice reçoit l’état courant et une action, puis renvoie l’état suivant. Chaque cas correspond à une transition nommée. Le cas par défaut laisse l’état inchangé, ce qui évite qu’une action inconnue ne provoque une modification accidentelle.
Produire un nouvel état
Pour chaque changement, le code construit un nouvel objet d’état et une nouvelle liste de tâches. L’opérateur de décomposition conserve les autres propriétés éventuelles de l’état ; map remplace la tâche ciblée et filter retire celle qui est supprimée. Les objets précédents ne sont pas modifiés sur place : vous pouvez donc comparer l’état avant et après une action.
Les actions sans effet, comme la suppression d’un identifiant absent, renvoient l’état courant. Le même choix est appliqué à un libellé vide après nettoyage ou à un identifiant déjà utilisé lors d’un ajout. Ces vérifications rendent les transitions plus robustes sans ajouter de règles dans les composants qui les déclencheront.
Faire évoluer l’état dans le point d’entrée
Remplacez le contenu de js/main.js par cet exemple complet. Il applique successivement trois actions à une variable d’état, puis affiche le résultat dans la console du navigateur. Aucune modification du DOM n’est nécessaire pour observer la transition.
import { etatInitial, calculerProgression } from "./etat.js";
import { reduireEtat } from "./reducteur.js";
let etat = etatInitial;
etat = reduireEtat(etat, {
type: "tache/ajouter",
id: "tache-1",
libelle: "Préparer le compte rendu"
});
etat = reduireEtat(etat, {
type: "tache/ajouter",
id: "tache-2",
libelle: "Envoyer le compte rendu"
});
etat = reduireEtat(etat, {
type: "tache/basculer",
id: "tache-1"
});
console.log("État courant :", etat);
console.log("Progression :", calculerProgression(etat.taches));État courant : { taches: [ { id: "tache-1", libelle: "Préparer le compte rendu", terminee: true }, { id: "tache-2", libelle: "Envoyer le compte rendu", terminee: false } ] }
Progression : { terminees: 1, total: 2, pourcentage: 50 }La variable etat reçoit la valeur renvoyée après chaque action ; vous pouvez ainsi suivre l’ordre des transitions. Le calcul de progression existant utilise ensuite la liste obtenue, sans enregistrer le pourcentage dans l’état. Le point d’entrée orchestre ici les appels, tandis que les règles de mise à jour restent regroupées dans js/reducteur.js.
La forme de l’état et les calculs dérivés ont été définis dans « Définir la forme de l’état ». À l’étape suivante, vous vérifierez les transitions sans navigateur et pourrez contrôler les cas ordinaires comme les actions sans effet.
À retenir
- Une action nomme une intention et transporte les données nécessaires à son traitement.
- La fonction réductrice reçoit l’état et l’action, puis renvoie l’état suivant.
- Construisez de nouvelles listes et de nouveaux objets au lieu de modifier l’état existant.
- Laissez les calculs d’affichage, comme la progression, dérivés de la liste des tâches.