Débutant30 min de lecture

État avec useState

Apprends à rendre tes composants React interactifs en gérant des données dynamiques avec le hook useState.

Pourquoi avons-nous besoin d'état ?

Jusqu'à présent, nos composants n'affichaient que des données statiques transmises via les props. Mais les vraies applications doivent répondre aux actions de l'utilisateur — incrémenter un compteur, basculer un menu, filtrer une liste, taper dans une barre de recherche. Pour cela, nous avons besoin d'un état.

L'état (state) est une donnée qui appartient à un composant et peut changer au fil du temps. Quand l'état change, React re-rend automatiquement le composant pour refléter les nouvelles données. C'est le cœur de la réactivité de React.

Considère cette tentative ratée de créer un compteur :

jsx
function Counter() {
  let count = 0;
  
  function handleClick() {
    count = count + 1;
    console.log(count); // Ceci s'incrémente, mais...
  }
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

Cliquer sur le bouton modifie la variable count, mais l'écran ne se met jamais à jour. Pourquoi ? Parce que React ne sait pas que la variable a changé — il n'a aucune raison de re-rendre le composant.

Pour résoudre cela, React fournit le hook useState. Les hooks sont des fonctions spéciales qui te permettent de te "connecter" aux fonctionnalités de React depuis les composants fonction. useState est le hook le plus fondamental — il donne à ton composant un morceau d'état que React suit et re-rend quand il change.

La différence entre props et state :

PropsState
Passées depuis le parentGérées à l'intérieur du composant
En lecture seule (immuables)Peuvent être mises à jour par le composant
Le parent les contrôleLe composant les contrôle
Changer les props re-rend l'enfantChanger l'état re-rend le composant

Utiliser le hook useState

Le hook useState est appelé à l'intérieur de ta fonction composant. Il prend une valeur initiale comme argument et retourne un tableau avec exactement deux éléments :

  1. La valeur actuelle de l'état
  2. Une fonction pour mettre à jour l'état
jsx
const [count, setCount] = React.useState(0);

Ceci utilise la déstructuration de tableau pour nommer les deux valeurs. La convention est [quelqueChose, setQuelqueChose].

Voici le compteur qui fonctionne :

jsx
function Counter() {
  const [count, setCount] = React.useState(0);
  
  function handleClick() {
    setCount(count + 1);
  }
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

Quand tu appelles setCount(count + 1), React :

  1. Met à jour la valeur de l'état en interne.
  2. Re-rend le composant — rappelle ta fonction.
  3. Cette fois, useState(0) retourne la nouvelle valeur au lieu de 0.
  4. Le JSX reflète maintenant le compteur mis à jour.

La valeur initiale (0 dans ce cas) est uniquement utilisée lors du premier rendu. Lors des rendus suivants, useState retourne la valeur actuelle de l'état.

Règles importantes pour les hooks :

  • Appelle uniquement les hooks au niveau supérieur de ton composant — jamais à l'intérieur de boucles, conditions ou fonctions imbriquées.
  • Appelle uniquement les hooks depuis des composants fonction React ou d'autres hooks personnalisés.
jsx
// FAUX — hook à l'intérieur d'une condition
function Bad() {
  if (someCondition) {
    const [value, setValue] = React.useState(0); // Ne fais jamais ça !
  }
}

// CORRECT — toujours au niveau supérieur
function Good() {
  const [value, setValue] = React.useState(0);
  // Utilise les conditions dans le JSX ou la logique du gestionnaire à la place
}

L'état est immuable : remplace, ne mute pas

Un concept critique dans React : tu ne dois jamais muter l'état directement. Utilise toujours la fonction setter pour créer une nouvelle valeur. React se base sur la détection du changement de référence pour savoir quand re-rendre.

Avec les valeurs primitives (nombres, chaînes de caractères, booléens), c'est naturel :

jsx
const [count, setCount] = React.useState(0);
setCount(count + 1); // Crée un nouveau nombre

const [name, setName] = React.useState('');
setName('Alice'); // Crée une nouvelle chaîne

Avec les objets et tableaux, tu dois créer une nouvelle copie au lieu de modifier l'existante :

jsx
// FAUX — muter l'objet existant
const [user, setUser] = React.useState({ name: 'Alice', age: 25 });
user.age = 26; // On mute l'objet !
setUser(user); // React voit la même référence — pas de re-rendu !

// CORRECT — créer un nouvel objet avec l'opérateur spread
setUser({ ...user, age: 26 }); // Nouvel objet, React re-rend

La même chose s'applique aux tableaux :

jsx
const [items, setItems] = React.useState(['A', 'B']);

// FAUX
items.push('C');
setItems(items); // Même référence !

// CORRECT
setItems([...items, 'C']); // Nouveau tableau

Mises à jour fonctionnelles : Quand le nouvel état dépend de l'état précédent, utilise la forme callback du setter pour éviter les valeurs obsolètes :

jsx
// Forme basique — bien pour les cas simples
setCount(count + 1);

// Mise à jour fonctionnelle — plus sûre, surtout dans les situations asynchrones
setCount(prevCount => prevCount + 1);

La forme fonctionnelle reçoit la valeur de l'état précédent la plus à jour, ce qui évite les bugs quand plusieurs mises à jour se produisent dans le même cycle de rendu.

Plusieurs variables d'état

Un composant peut avoir autant d'appels useState que nécessaire. Chacun gère un morceau d'état indépendant :

jsx
function UserForm() {
  const [name, setName] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [age, setAge] = React.useState(0);
  
  return (
    <form>
      <input value={name} onChange={e => setName(e.target.value)} />
      <input value={email} onChange={e => setEmail(e.target.value)} />
      <input 
        type="number" 
        value={age} 
        onChange={e => setAge(Number(e.target.value))} 
      />
    </form>
  );
}

Chaque variable d'état est complètement indépendante — mettre à jour name n'affecte pas email ou age.

Quand utiliser un état vs. plusieurs :

  • Utilise des variables d'état séparées quand les valeurs changent indépendamment. Un champ nom et un champ âge sont mis à jour séparément.
  • Utilise un seul objet d'état quand les valeurs sont étroitement liées et changent toujours ensemble :
jsx
const [position, setPosition] = React.useState({ x: 0, y: 0 });

// Mettre à jour les deux en même temps :
setPosition({ x: event.clientX, y: event.clientY });

État vs. variables normales :

Toutes les données n'ont pas besoin d'être dans l'état. Utilise l'état uniquement pour les données qui :

  1. Changent au fil du temps — Si une valeur ne change jamais, utilise une const normale.
  2. Affectent la sortie rendue — Si changer la valeur doit mettre à jour l'UI, ça devrait être de l'état.
  3. Ne peuvent pas être calculées à partir d'autres états ou props — Si tu peux calculer une valeur à partir d'un état existant, utilise une variable normale au lieu de créer un état redondant.
jsx
function Cart({ items }) {
  // Valeur dérivée — PAS de l'état, calculée depuis les props
  const total = items.reduce((sum, item) => sum + item.price, 0);
  
  // CECI EST de l'état — change en réponse à une action utilisateur
  const [couponCode, setCouponCode] = React.useState('');
  
  return <p>Total: ${total}</p>;
}

useState en action

html
<div id="root"></div>
<script src="https://unpkg.com/react@19/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@19/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
  function Counter() {
    const [count, setCount] = React.useState(0);

    return (
      <div style={{ textAlign: "center", padding: "20px" }}>
        <h2>Counter: {count}</h2>
        <button onClick={() => setCount(count - 1)}>- Decrease</button>
        {" "}
        <button onClick={() => setCount(0)}>Reset</button>
        {" "}
        <button onClick={() => setCount(count + 1)}>+ Increase</button>
      </div>
    );
  }

  function App() {
    return <Counter />;
  }

  ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>

Pourquoi muter directement une variable d'état (comme `count = count + 1`) ne met-il pas à jour l'UI ?

Prêt à pratiquer ?

Crée ton compte gratuit pour accéder à l'éditeur de code interactif, lancer les défis et suivre ta progression.