⚡ Qu'est-ce que le state ?

Le state (état) est la mémoire locale d'un composant — il représente les données qui peuvent changer au fil du temps et qui déclenchent un re-rendu quand elles sont mises à jour.

  • Props — données passées depuis le parent (en lecture seule)
  • State — données locales qui peuvent être modifiées par le composant lui-même
  • Quand le state change, React re-render le composant automatiquement
⚠️ Ne jamais modifier le state directement : count = count + 1 ne déclenche pas de re-render ! Toujours passer par le setter : setCount(count + 1).

📝 Syntaxe — useState

useState retourne un tableau avec deux éléments : la valeur actuelle et la fonction pour la modifier.

import { useState } from 'react';

function Compteur() {
  // [valeurActuelle, fonctionMiseAJour] = useState(valeurInitiale)
  const [count, setCount] = useState(0);
  const [nom, setNom] = useState('');
  const [actif, setActif] = useState(false);

  return (
    <div>
      <p>Compteur : {count}</p>
      <button onClick={() => setCount(count + 1)}>Incrémenter</button>

      <input value={nom} onChange={e => setNom(e.target.value)} />
      <p>Bonjour {nom}</p>

      <button onClick={() => setActif(!actif)}>
        {actif ? 'Désactiver' : 'Activer'}
      </button>
    </div>
  );
}

🔄 Mise à jour du state

Deux façons de mettre à jour : valeur directe ou fonction updater (recommandée quand la nouvelle valeur dépend de l'ancienne).

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

  // ❌ Problème : stale closure — peut lire une ancienne valeur
  function incrementerDirect() {
    setCount(count + 1);
    setCount(count + 1); // compte toujours comme 1, pas 2 !
  }

  // ✅ Correct : fonction updater — toujours basée sur la dernière valeur
  function incrementerCorrect() {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1); // compte correctement +2
  }

  // Mise à jour conditionnelle
  function incrementerSiInferieur(max) {
    setCount(prev => prev < max ? prev + 1 : prev);
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={incrementerCorrect}>+1</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
✅ Utiliser la forme setCount(prev => prev + 1) dès que la nouvelle valeur dépend de l'ancienne — c'est toujours plus sûr.

📦 State avec objets et tableaux

Le state en React doit être traité comme immutable — ne jamais muter directement, toujours créer une nouvelle copie.

// State objet — toujours créer un nouvel objet
const [utilisateur, setUtilisateur] = useState({ nom: '', age: 0 });

// ❌ Mutation directe — ne PAS faire ça
utilisateur.nom = 'Alice'; // React ne le détecte pas

// ✅ Spread operator — crée un nouvel objet
setUtilisateur({ ...utilisateur, nom: 'Alice' });

// State tableau — toujours créer un nouveau tableau
const [items, setItems] = useState([]);

// Ajouter
setItems(prev => [...prev, nouvelItem]);

// Supprimer
setItems(prev => prev.filter(item => item.id !== idASupprimer));

// Modifier
setItems(prev => prev.map(item =>
  item.id === idACibler ? { ...item, done: true } : item
));

🧮 State dérivé

Ne pas stocker dans le state ce qui peut être calculé depuis le state existant — utiliser des variables simples à la place.

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, texte: 'Apprendre React', done: true },
    { id: 2, texte: 'Créer un projet', done: false },
  ]);

  // ✅ State dérivé — calculé depuis todos (pas besoin de useState)
  const total = todos.length;
  const termines = todos.filter(t => t.done).length;
  const progression = total > 0 ? Math.round(termines / total * 100) : 0;

  return (
    <div>
      <p>{termines}/{total} — {progression}%</p>
      {/* liste des todos */}
    </div>
  );
}
✅ Règle : ne mettre dans le state que ce qui ne peut pas être calculé depuis du state déjà existant.

⬆️ Remonter le state (Lifting State Up)

Quand plusieurs composants doivent partager du state, on le déplace vers leur ancêtre commun le plus proche.

// ✅ Le state est dans le parent commun
function App() {
  const [temperature, setTemperature] = useState(22);

  return (
    <div>
      {/* Les deux composants partagent le même state */}
      <Celsius temp={temperature} onChange={setTemperature} />
      <Fahrenheit temp={temperature * 9/5 + 32} />
    </div>
  );
}

function Celsius({ temp, onChange }) {
  return (
    <input
      type="number"
      value={temp}
      onChange={e => onChange(Number(e.target.value))}
    />
  );
}

function Fahrenheit({ temp }) {
  return <p>{temp.toFixed(1)} °F</p>;
}
📝 Exercices R03 ▶ Mini-projet : Todo List 🧠 QCM R03 Suivant : useEffect →