Contactez-nous

Mises à jour fonctionnelles pour garantir la fraîcheur de l'état

Apprenez à utiliser les mises à jour fonctionnelles de useState (setState(prevState => ...)) pour garantir la fraîcheur de l'état et éviter les bugs de stale state en React.

Le défi : Le risque de l'état obsolète (Stale State)

Lorsque vous mettez à jour l'état avec `useState`, il est courant que la nouvelle valeur dépende de la valeur précédente. Par exemple, incrémenter un compteur ou ajouter un élément à une liste. L'approche la plus directe pourrait sembler être de lire la variable d'état actuelle et de l'utiliser pour calculer la nouvelle valeur :

const [count, setCount] = useState(0);

function increment() {
  // Tentative directe (peut être problématique)
  setCount(count + 1); 
}

Cependant, cette approche présente un risque subtil mais important. Les mises à jour d'état déclenchées par `setState` ne sont pas toujours immédiates et synchrones. React peut les regrouper (batching) pour optimiser les performances, en particulier à l'intérieur des gestionnaires d'événements. Cela signifie que si vous appelez `setCount` plusieurs fois rapidement, la variable `count` que vous lisez dans votre fonction `increment` pourrait ne pas refléter la dernière valeur d'état mise en file d'attente par React. Vous pourriez lire une valeur obsolète (stale state), conduisant à des calculs incorrects.

Imaginez un scénario où vous voulez incrémenter le compteur de 3 en un seul clic :

function incrementTroisFois() {
  setCount(count + 1); // Si count vaut 0 ici...
  setCount(count + 1); // ... il peut encore valoir 0 ici !
  setCount(count + 1); // ... et encore 0 ici !
  // Résultat potentiel : count passe à 1 au lieu de 3.
}

Dans cet exemple, si React regroupe ces trois appels, la valeur de `count` lue à chaque appel pourrait être la même (celle avant le début du batch), annulant l'effet des incrémentations précédentes dans la même passe.

La solution : La forme fonctionnelle de mise à jour

Pour résoudre ce problème et garantir que vos mises à jour se basent toujours sur la valeur la plus récente de l'état, `useState` propose une syntaxe alternative pour sa fonction de mise à jour : la forme fonctionnelle.

Au lieu de passer directement la nouvelle valeur, vous passez une fonction. Cette fonction recevra automatiquement l'état précédent (garanti comme étant le plus à jour au moment de l'exécution de la mise à jour par React) comme argument. La valeur retournée par cette fonction deviendra le nouvel état.

La syntaxe est la suivante :

setEtat(prevState => {
  // Calculer le nouvel état basé sur prevState
  const newState = /* ... logique utilisant prevState ... */;
  return newState;
});

// Version concise pour des calculs simples
setEtat(prevState => prevState + 1);

Reprenons notre exemple d'incrémentation triple, cette fois avec la forme fonctionnelle :

function incrementTroisFoisCorrectement() {
  setCount(prevCount => prevCount + 1); // Reçoit la dernière valeur (0), retourne 1
  setCount(prevCount => prevCount + 1); // Reçoit la dernière valeur (1), retourne 2
  setCount(prevCount => prevCount + 1); // Reçoit la dernière valeur (2), retourne 3
  // Résultat garanti : count passe bien à 3.
}

Avec cette approche, même si React regroupe les mises à jour, chaque appel à la fonction de mise à jour recevra comme `prevCount` la valeur résultant de la mise à jour précédente dans la file d'attente. Cela garantit la correction du calcul, indépendamment du timing ou du batching des mises à jour.

Pourquoi et quand utiliser les mises à jour fonctionnelles ?

Il est fortement recommandé d'utiliser la forme fonctionnelle de mise à jour chaque fois que le nouvel état dépend de l'état précédent.

Les avantages principaux sont :

  • Fiabilité : Elimine les bugs liés au "stale state" causés par les mises à jour asynchrones ou groupées. Vous êtes certain de travailler avec la valeur d'état la plus récente au moment du calcul.
  • Prévisibilité : Le comportement de vos mises à jour d'état devient plus prévisible, indépendamment des optimisations internes de React.
  • Robustesse : Votre code est moins susceptible de casser si la logique de mise à jour devient plus complexe ou si elle est appelée dans des contextes où le batching est plus fréquent.

Même si dans certains cas simples, la mise à jour directe peut sembler fonctionner, prendre l'habitude d'utiliser la forme fonctionnelle pour les mises à jour dépendantes de l'état précédent est une bonne pratique qui renforce la robustesse de vos composants React.