Contactez-nous

Mise à jour basée sur l'état précédent

Apprenez à utiliser la forme fonctionnelle du setter de useState (setEtat(prevState => ...)) pour garantir des mises à jour d'état fiables lorsqu'elles dépendent de la valeur précédente.

Le défi des mises à jour dépendantes

Nous avons vu que les mises à jour d'état avec la fonction setter (`setEtat`) peuvent être asynchrones et regroupées par React. Cela pose un problème potentiel lorsque la nouvelle valeur de l'état dépend de sa valeur précédente. Prenons l'exemple classique d'un compteur que nous voulons incrémenter plusieurs fois suite à une seule action :

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

  const incrementerTroisFois = () => {
    // On pourrait s'attendre à ce que cela ajoute 3
    setCount(count + 1); // Rappel: 'count' ici vaut 0 (valeur du rendu actuel)
    setCount(count + 1); // 'count' vaut toujours 0 ici
    setCount(count + 1); // 'count' vaut toujours 0 ici
    
    // Résultat : React reçoit trois demandes pour mettre l'état à 0 + 1 = 1.
    // Au prochain rendu, 'count' vaudra 1, pas 3 !
  };

  return (
    

Compte : {count}

); }

Ce code ne fonctionne pas comme prévu car chaque appel à `setCount(count + 1)` utilise la même valeur de `count` (celle du rendu en cours) comme base de calcul. React voit juste trois requêtes pour passer l'état à `1`.

Comment garantir que chaque mise à jour se base sur la version la plus récente de l'état, même si elles sont traitées en lot ou de manière asynchrone ? C'est là qu'intervient la forme fonctionnelle de la mise à jour d'état.

La solution : La forme fonctionnelle (`setEtat(prevState => ...)`)

Au lieu de passer directement la nouvelle valeur à la fonction setter, vous pouvez lui passer une fonction. Cette fonction recevra automatiquement l'état précédent (previous state) comme unique argument et devra retourner la nouvelle valeur de l'état.

setEtat(prevState => {
  // prevState contient la valeur la plus à jour de l'état
  // Calculez et retournez la nouvelle valeur de l'état ici
  return nouvelleValeur; 
});

React garantit que la valeur `prevState` passée à votre fonction est bien l'état le plus récent, même si plusieurs mises à jour sont en attente. Il mettra en file d'attente ces fonctions de mise à jour et les appliquera séquentiellement.

Exemple corrigé du compteur multiple

Appliquons cette forme fonctionnelle à notre exemple précédent :

import React, { useState } from 'react';

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

  const incrementerTroisFois = () => {
    // Utilisation de la forme fonctionnelle pour chaque mise à jour
    setCount(prevCount => prevCount + 1);
    setCount(prevCount => prevCount + 1);
    setCount(prevCount => prevCount + 1);
    
    // Fonctionnement interne (simplifié) :
    // 1. File d'attente : [ c => c + 1 ]
    // 2. File d'attente : [ c => c + 1, c => c + 1 ]
    // 3. File d'attente : [ c => c + 1, c => c + 1, c => c + 1 ]
    // React traite la file : 0 -> 1 -> 2 -> 3
    // Au prochain rendu, 'count' vaudra 3.
  };

  return (
    

Compte : {count}

); } export default CompteurMultipleCorrect;

En utilisant `prevCount => prevCount + 1` pour chaque appel, nous nous assurons que chaque incrémentation se base sur le résultat de la précédente, même si elles sont traitées dans le même lot par React. Le résultat est maintenant correct : le compteur augmente bien de 3 à chaque clic.

(Note : `prevCount` est un nom de variable courant pour l'argument, mais vous pouvez l'appeler comme vous voulez, par exemple `etatPrecedent`, `currentCount`, etc.)

Quand utiliser la forme fonctionnelle ?

La règle générale est simple : Utilisez la forme fonctionnelle de mise à jour (`setEtat(prevState => ...)`) chaque fois que votre nouvelle valeur d'état dépend de la valeur précédente.

Cela inclut les cas comme :

  • Incrémenter/décrémenter un compteur.
  • Basculer une valeur booléenne (`setIsActive(prev => !prev)`).
  • Ajouter un élément à un tableau dans l'état (`setItems(prevItems => [...prevItems, nouvelItem])`).
  • Modifier une propriété d'un objet dans l'état en se basant sur l'objet précédent (`setConfig(prevConfig => ({...prevConfig, theme: 'dark'}))`).
  • Toute logique où vous lisez la variable d'état actuelle pour calculer la prochaine valeur.

Si la nouvelle valeur ne dépend pas du tout de l'ancienne (par exemple, réinitialiser un compteur à 0 avec `setCount(0)`, ou définir le contenu d'un champ de saisie avec `setNom(event.target.value)`), alors passer directement la nouvelle valeur (`setEtat(nouvelleValeur)`) est parfaitement acceptable et légèrement plus concis.

Conclusion : Garantir la fiabilité des transitions d'état

La forme fonctionnelle de mise à jour de `useState` (`setEtat(prevState => newState)`) est un outil essentiel pour gérer correctement les transitions d'état qui dépendent de la valeur précédente. En raison de la nature potentiellement asynchrone et groupée des mises à jour d'état dans React, cette approche garantit que vos calculs se basent toujours sur la valeur la plus récente, évitant ainsi les bugs subtils et assurant la fiabilité de la logique de votre composant.

Prendre l'habitude d'utiliser cette forme lorsque nécessaire est une marque de compréhension approfondie du fonctionnement de l'état dans React.