Les fermetures
Une fonction qui se souvient : le secret qui se cache derrière
useState.
Objectifs de la séance
- comprendre qu'une fonction garde l'accès aux variables de l'endroit où elle a été créée ;
- écrire une fonction qui fabrique une autre fonction ;
- fabriquer votre propre mini
useState; - reconnaître le piège de la valeur périmée (stale value).
Petite vérification
Avant de commencer, vérifiez si vous savez...
- dans
labo-js/modules/, exécuternode main.jset lire la ligne affichée - supprimer le mot
exportdevantcalculerPourcentagedanscalculs.js, relancernode main.js, puis lire le message d'erreur - dire quel type d'export (nommé ou par défaut) s'importe sans accolades
📌 Réponse attendue
Notions théoriques
Une fonction peut se souvenir
Observez ce code :
function creerCompteur() {
let valeur = 0; // variable locale à creerCompteur
return () => {
valeur = valeur + 1;
return valeur;
};
}
const compteur = creerCompteur();
console.log(compteur()); // 1
console.log(compteur()); // 2
console.log(compteur()); // 3
Étonnant : creerCompteur est terminée depuis longtemps, et pourtant la fonction renvoyée se souvient de valeur et la fait augmenter à chaque appel.
C'est une fermeture (closure) : une fonction garde un accès permanent aux variables de l'endroit où elle a été créée, même après la fin de la fonction parente.
Image à retenir : la fonction emporte avec elle un petit sac à dos contenant les variables dont elle a besoin.
Chaque appel crée un sac à dos différent
const compteurA = creerCompteur();
const compteurB = creerCompteur();
console.log(compteurA()); // 1
console.log(compteurA()); // 2
console.log(compteurB()); // 1 (compteurB a sa propre variable valeur)
Les deux compteurs sont indépendants : chacun a sa propre variable valeur, invisible de l'extérieur. On ne peut pas la modifier par accident.
Votre propre mini useState
Voici comment React mémorise une valeur entre deux affichages. Cette version simplifiée utilise une fermeture et une destructuration de tableau :
function creerEtat(valeurInitiale) {
let valeur = valeurInitiale;
const lire = () => valeur;
const ecrire = (nouvelleValeur) => {
valeur = nouvelleValeur;
};
return [lire, ecrire];
}
const [lireScore, ecrireScore] = creerEtat(0);
console.log(lireScore()); // 0
ecrireScore(5);
console.log(lireScore()); // 5
Le vrai useState(0) renvoie aussi un tableau de deux éléments : la valeur et une fonction pour la modifier. La mémoire de ce couple
est entretenue par React grâce au même principe. Vous le découvrirez dans la dernière partie.
Le piège : la valeur périmée
Une fonction qui reçoit une valeur en paramètre en garde une photo : si la variable d'origine change ensuite, la photo reste identique.
function creerAffichage(score) {
return () => console.log(`Score : ${score}`);
}
let scoreActuel = 0;
const afficher = creerAffichage(scoreActuel); // photo : 0
scoreActuel = 5;
afficher(); // Score : 0 (valeur périmée !)
Ce phénomène est la cause d'un bug classique en React : un callback créé lors d'un affichage garde la valeur de cet affichage-là. La solution (une mise à jour qui dépend de la valeur la plus récente) sera étudiée dans la partie 7.
Les fermetures sont puissantes, mais une variable cachée est une variable qu'on ne voit pas. Utilisez-les quand c'est nécessaire (un état, un compteur, une minuterie) et gardez le reste du code en fonctions pures qui reçoivent tout par leurs paramètres.
Test de mémorisation/compréhension
TP pour réfléchir et résoudre des problèmes
Votre mission : fabriquer un compteur de score puis votre mini useState. Travaillez dans le fichier labo-js/fermetures.js.
Étape 1 — Un compteur de bonnes réponses
Complétez creerCompteur : la fonction renvoyée doit ajouter 1 puis renvoyer la nouvelle valeur.
Personne, hors de creerCompteur, ne peut écrire valeur = 1000. Seule la fonction renvoyée peut la modifier, et seulement en ajoutant 1 : le score ne peut pas être triché depuis l'extérieur.
Étape 2 — Deux compteurs indépendants
Créez un deuxième compteur pour les mauvaises réponses et vérifiez qu'il est indépendant du premier.
Appeler la fonction fabrique deux fois donne deux états séparés. Dans React, chaque composant affiché a de la même façon son propre état.
Étape 3 — Votre mini useState
Complétez la fonction creerEtat pour qu'elle renvoie le couple [lire, ecrire].
Ici, lire est une fonction : elle regarde la valeur à l'instant de l'appel. Si on avait renvoyé directement valeur, on aurait récupéré une photo figée, c'est-à-dire le piège de la valeur périmée.
📌 Une solution
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
| Fermeture | Une fonction garde l'accès aux variables de l'endroit où elle a été créée. |
| Fabrique de fonctions | Une fonction qui renvoie une fonction crée un état privé, propre à chaque appel. |
Mini useState | creerEtat renvoie [lire, ecrire] : le même principe que [valeur, setValeur] en React. |
| Valeur périmée | Une valeur passée en paramètre est une photo : elle ne suit pas les changements ultérieurs. |
Aperçu de la prochaine séance
Que se passe-t-il quand une donnée est invalide, qu'un fichier est introuvable ou qu'un serveur ne répond pas ? Dans la prochaine séance, vous allez
apprendre à lever et à attraper les erreurs avec throw et try/catch, pour que votre application reste debout.