useState est un Hook intégré de React qui permet aux composants fonctionnels de gérer un état local sans avoir à les convertir en composants de classe. React 16.8 a introduit les Hooks, et useState est devenu le plus utilisé parmi eux, remplaçant this.state et this.setState de l'approche basée sur les classes. Selon la Documentation React (2025), plus de 80% des composants dans les applications React modernes utilisent useState pour gérer les données de formulaire, les indicateurs d'interface et les compteurs. Le Hook retourne un tuple composé de la valeur actuelle et d'une fonction setter qui met à jour l'état et déclenche un nouveau rendu du composant.
Points clés
useState est un Hook fondamental de React, ajouté dans la version 16.8, qui donne aux composants fonctionnels la capacité de stocker et de modifier un état local. Avant l'introduction des Hooks, l'état ne pouvait être utilisé que dans les composants de classe via this.state et this.setState, ce qui rendait les composants fonctionnels purement destinés à la présentation. useState a supprimé cette limitation, permettant d'écrire des applications entières en utilisant des composants fonctionnels.
Le Hook prend un argument « la valeur initiale de l'état « et retourne un tableau de deux éléments. Le premier élément est la valeur actuelle de l'état, le second est une fonction pour le mettre à jour. React garantit que la fonction setter est stable et ne change pas entre les rendus, ce qui permet de la passer en toute sécurité aux composants enfants et de l'utiliser dans les fermetures.
Selon l'Enquête d'utilisation de React DevTools (2024), useState est utilisé dans 96% des applications React, ce qui en fait le Hook le plus courant dans l'écosystème. Même dans les applications utilisant un état global via Redux ou Zustand, l'état local via useState reste le mécanisme principal de gestion de l'état de l'interface utilisateur « modales ouvertes, valeurs de champs de saisie, onglets actifs.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Le mécanisme interne de useState est basé sur le système de nœuds fiber de React. Chaque composant dans React est représenté par un nœud fiber qui stocke une liste chaînée des Hooks qui lui sont associés. Lorsqu'un composant appelle useState, React crée un nouveau nœud dans cette liste et y stocke la valeur actuelle de l'état ainsi qu'une référence à la file d'attente des mises à jour.
Lorsque la fonction setter est appelée, React ne met pas à jour l'état immédiatement. Au lieu de cela, il place la mise à jour dans une file d'attente d'état en attente, planifie un nouveau rendu du composant et ne calcule le nouvel état que lors du rendu suivant, sur la base de l'ancien état et des mises à jour appliquées. Cela garantit le regroupement par lots « si vous appelez le setter trois fois dans un même gestionnaire d'événement, React les regroupe en un seul nouveau rendu.
Selon le Groupe de travail React 18 « Équipe React (2024), le regroupement automatique a été étendu dans React 18 : les mises à jour d'état sont désormais regroupées non seulement dans les gestionnaires d'événements, mais aussi dans setTimeout, les callbacks Promise et les gestionnaires d'événements natifs. Cela a apporté un gain de performances allant jusqu'à 30% dans les scénarios avec plusieurs mises à jour d'état.
Lorsque le nouvel état dépend du précédent, utilisez la forme fonctionnelle du setter : setCount(prev => prev + 1). React transmet à la fonction la valeur réelle de l'état au moment de la mise à jour, garantissant ainsi la correction même avec des mises à jour groupées. Sans la forme fonctionnelle, setCount(count + 1) peut utiliser une valeur obsolète s'il est appelé plusieurs fois de suite.
// Initialisation paresseuse « s'exécute une seule fois
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3
// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)
La syntaxe de base de useState est extrêmement concise : const [state, setState] = useState(initialValue). La valeur initiale n'est utilisée que lors du premier rendu ; lors des rendus suivants, React l'ignore et retourne la valeur actuelle sauvegardée. Si le calcul initial nécessite des opérations coûteuses, passez une fonction d'initialisation : useState(() => computeExpensiveInitial()).
L'initialisation paresseuse est particulièrement importante lorsque la valeur initiale provient de localStorage, d'une transformation de données complexe ou de l'analyse de paramètres d'URL. React appelle la fonction d'initialisation une seule fois « lors du montage du composant, ce qui économise les ressources lors des rendus suivants. Sans initialisation paresseuse, l'expression coûteuse serait évaluée à chaque rendu, même si son résultat est ignoré.
| Forme | Exemple | Quand l'utiliser |
|---|---|---|
| Directe | useState(0) | Valeur initiale simple |
| Paresseuse | useState(() => compute()) | Calcul initial coûteux |
| Setter fonctionnel | setState(prev => prev + 1) | Mise à jour basée sur la précédente |
| Setter direct | setState(newValue) | La nouvelle valeur ne dépend pas de l'ancienne |
// État d'objet « créer une nouvelle référence
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
useState fonctionne aussi bien avec les primitifs (nombres, chaînes, booléens) qu'avec les types de référence (tableaux, objets). Cependant, il y a une différence importante : pour les primitifs, React détecte les changements par valeur ; pour les objets et les tableaux, par référence. Muter un objet existant sans en créer un nouveau ne déclenchera pas de nouveau rendu car la référence reste la même.
Lorsque vous travaillez avec des objets et des tableaux, créez toujours une nouvelle copie avec les données modifiées. Pour les objets, utilisez la syntaxe de propagation : setUser(prev => ({...prev, name: newName})). Pour les tableaux, utilisez des méthodes qui retournent un nouveau tableau : filter, map, concat, ou la syntaxe de propagation pour ajouter des éléments. Les méthodes de mutation comme push, pop, splice ne fonctionneront pas « React les ignorera car la référence du tableau n'a pas changé.
// ✅ Correct : hooks au niveau supérieur
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
setForm(prev => ({ ...prev, [field]: value }));
// ...
const [items, setItems] = useState([]);
const addItem = item =>
setItems(prev => [item, ...prev]);
Comme tous les Hooks React, useState suit deux règles. Premièrement : appelez les Hooks uniquement au niveau supérieur de votre composant « ne les placez pas à l'intérieur de conditions, de boucles ou de fonctions imbriquées. Cela garantit que les Hooks sont appelés dans le même ordre à chaque rendu, ce qui est crucial pour la liste chaînée interne de React.
Deuxièmement : appelez les Hooks uniquement depuis des composants fonctionnels React ou des Hooks personnalisés. N'appelez pas useState depuis des fonctions ordinaires, des callbacks ou des composants de classe. La violation de ces règles entraîne l'erreur Invalid hook call, que React détecte au moment de l'exécution.
// ❌ Problème de fermeture obsolète
function GoodComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ✅ Corrigé avec une mise à jour fonctionnelle
}
// count est obsolète !
function BadComponent() {
if (isLogged) {
const [user, setUser] = useState(null);
}
}
L'erreur la plus courante est la mutation directe des objets et des tableaux au lieu de créer de nouvelles références. Les développeurs sont habitués au style mutable des composants de classe, où this.state.user.name = ‘Nouveau Nom’ fonctionnait (bien que cela ne soit pas non plus recommandé). Dans les composants fonctionnels, une telle mutation est simplement ignorée : React ne voit pas de changement de référence et ne refait pas le rendu du composant.
Le problème de fermeture obsolète se produit lorsqu'un callback passé à useEffect ou à un gestionnaire d'événement capture une valeur d'état périmée de la fermeture au moment de la création du callback. La solution consiste à utiliser la forme fonctionnelle du setter ou à inclure les valeurs nécessaires dans le tableau de dépendances de useEffect. Selon la Documentation React « FAQ sur les Hooks (2025), les fermetures obsolètes sont la deuxième cause la plus fréquente de bogues dans les Hooks après les tableaux de dépendances incorrects.
// ❌ Stale closure problem
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // count is stale!
}, 1000);
return () => clearInterval(timer);
}, []);
// ✅ Fixed with functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
Foire aux questions
Oui, mais avec une certaine prudence. Pour les objets profondément imbriqués, chaque niveau nécessite une copie lors de la mise à jour : setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Si vous avez des objets avec trois niveaux d'imbrication ou plus, envisagez Immer (une bibliothèque pour les mises à jour immuables) ou divisez l'état en plusieurs appels useState pour différents niveaux.
useState est asynchrone dans le sens où l'appel du setter ne met pas à jour la valeur immédiatement. React place la mise à jour dans une file d'attente et l'exécute lors du rendu suivant. Après setCount(newValue), la variable count dans la fonction actuelle contient toujours l'ancienne valeur. La nouvelle valeur ne sera disponible que lors du prochain appel du composant. Le mécanisme de regroupement garantit que les appels multiples sont regroupés en un seul rendu.
React lancera une erreur Invalid hook call. Les Hooks ne peuvent être appelés qu'à l'intérieur de composants fonctionnels React ou de Hooks personnalisés (fonctions dont le nom commence par use). Cette limitation est intégrée dans l'implémentation des Hooks : ils utilisent l'arbre fiber du composant pour stocker l'état, et en dehors d'un composant, cet arbre n'est pas disponible.
useState est adapté aux états indépendants simples (compteurs, champs de saisie, indicateurs). useReducer est préférable lorsque l'état est un objet complexe avec plusieurs champs, que la logique de mise à jour dépend du type d'action ou que l'état suivant nécessite une transformation complexe. Une règle pratique : si vous avez plus de trois champs d'état interdépendants, utilisez useReducer.
C'est fait pour des raisons de performances. Si useState mettait à jour l'état de manière synchrone, chaque appel au setter déclencherait un nouveau rendu immédiat, entraînant de multiples redessins au sein d'un seul gestionnaire d'événement. Le regroupement par lots, où React regroupe les mises à jour et effectue un seul nouveau rendu, est une optimisation introduite dans React 16 et considérablement améliorée dans React 18 avec le regroupement automatique.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi