Apprends à rendre tes composants React interactifs en gérant des données dynamiques avec le hook useState.
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 :
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 :
| Props | State |
|---|---|
| Passées depuis le parent | Gérées à l'intérieur du composant |
| En lecture seule (immuables) | Peuvent être mises à jour par le composant |
| Le parent les contrôle | Le composant les contrôle |
| Changer les props re-rend l'enfant | Changer l'état re-rend le composant |
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 :
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 :
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 :
useState(0) retourne la nouvelle valeur au lieu de 0.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 :
// 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
}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 :
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îneAvec les objets et tableaux, tu dois créer une nouvelle copie au lieu de modifier l'existante :
// 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-rendLa même chose s'applique aux tableaux :
const [items, setItems] = React.useState(['A', 'B']);
// FAUX
items.push('C');
setItems(items); // Même référence !
// CORRECT
setItems([...items, 'C']); // Nouveau tableauMises à 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 :
// 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.
Un composant peut avoir autant d'appels useState que nécessaire. Chacun gère un morceau d'état indépendant :
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 :
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 :
const normale.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>;
}<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 ?