Chapitre 1 : useState
Le problème
En JavaScript classique, vous pouvez modifier une variable :
- snippet.javascript
let count = 0; count = count + 1;
Mais en React, cela ne fonctionne pas :
- snippet.javascript
function Counter() { let count = 0; const increment = () => { count = count + 1; // La variable change... console.log(count); // Affiche bien 1, 2, 3... }; // Mais l'UI n'est JAMAIS mise à jour ! return ( <div> <p>{count}</p> {/* Affiche toujours 0 */} <button onClick={increment}>+1</button> </div> ); }
Pourquoi ? React ne sait pas que la variable a changé. Il faut lui dire explicitement.
La solution : useState
- snippet.javascript
import { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); // Dit à React de re-rendre }; return ( <div> <p>{count}</p> <button onClick={increment}>+1</button> </div> ); }
useState retourne un tableau avec :
- La valeur actuelle (
count) - Une fonction pour la modifier (
setCount)
Syntaxe
- snippet.javascript
const [state, setState] = useState(initialValue);
state: la valeur actuellesetState: fonction pour modifier la valeurinitialValue: valeur initiale (une seule fois au premier rendu)
Différents types d'état
Nombres
- snippet.javascript
const [count, setCount] = useState(0); setCount(count + 1); setCount(count - 1);
Chaînes
- snippet.javascript
const [name, setName] = useState(''); setName('Jean'); setName(e.target.value); // Pour un input
Booléens
- snippet.javascript
const [isOpen, setIsOpen] = useState(false); setIsOpen(true); setIsOpen(!isOpen); // Toggle
Objets
- snippet.javascript
const [user, setUser] = useState({ name: '', email: '' }); // INCORRECT - mutation directe user.name = 'Jean'; // Ne fonctionne pas ! // CORRECT - nouvel objet avec spread setUser({ ...user, name: 'Jean' });
Tableaux
- snippet.javascript
const [items, setItems] = useState([]); // Ajouter un élément setItems([...items, newItem]); // Supprimer un élément setItems(items.filter(item => item.id !== idToRemove)); // Modifier un élément setItems(items.map(item => item.id === idToUpdate ? { ...item, name: 'Nouveau nom' } : item ));
Mise à jour basée sur l'état précédent
Quand la nouvelle valeur dépend de l'ancienne, utilisez la forme fonctionnelle :
- snippet.javascript
// PROBLÈME POTENTIEL avec plusieurs appels rapides setCount(count + 1); setCount(count + 1); // count est toujours l'ancienne valeur ! // Résultat : +1 au lieu de +2 // CORRECT - forme fonctionnelle setCount(prevCount => prevCount + 1); setCount(prevCount => prevCount + 1); // Résultat : +2
La forme fonctionnelle garantit que vous travaillez avec la dernière valeur.
Plusieurs états dans un composant
- snippet.javascript
function LoginForm() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // ... }
Ou regroupés dans un objet :
- snippet.javascript
function LoginForm() { const [form, setForm] = useState({ email: '', password: '', isLoading: false, error: null }); const updateField = (field, value) => { setForm(prev => ({ ...prev, [field]: value })); }; // updateField('email', 'jean@example.com'); }
Initialisation paresseuse
Si la valeur initiale nécessite un calcul coûteux, passez une fonction :
- snippet.javascript
// PROBLÈME - calcul exécuté à chaque rendu const [data, setData] = useState(expensiveCalculation()); // CORRECT - calcul exécuté une seule fois const [data, setData] = useState(() => expensiveCalculation());
Comparaison avec PHP
| Concept | PHP (session) | React (useState) |
|---|---|---|
| Stockage | $_SESSION['count'] = 0 | useState(0) |
| Lecture | $_SESSION['count'] | count |
| Écriture | $_SESSION['count']++ | setCount(c => c + 1) |
| Persistance | Côté serveur | Mémoire navigateur |
Exercices
Exercice 1 : Compteur avec min/max
Créer un compteur qui :
- Ne peut pas descendre en dessous de 0
- Ne peut pas dépasser 10
Solution :
- snippet.javascript
function Counter() { const [count, setCount] = useState(0); const increment = () => { setCount(prev => Math.min(prev + 1, 10)); }; const decrement = () => { setCount(prev => Math.max(prev - 1, 0)); }; return ( <div> <button onClick={decrement} disabled={count === 0}>-</button> <span>{count}</span> <button onClick={increment} disabled={count === 10}>+</button> </div> ); }
Exercice 2 : Liste de tâches
Créer un composant qui :
- Affiche une liste de tâches
- Permet d'ajouter une tâche
- Permet de supprimer une tâche
Solution :
- snippet.javascript
function TodoList() { const [todos, setTodos] = useState([]); const [input, setInput] = useState(''); const addTodo = () => { if (input.trim()) { setTodos([...todos, { id: Date.now(), text: input }]); setInput(''); } }; const removeTodo = (id) => { setTodos(todos.filter(todo => todo.id !== id)); }; return ( <div> <input value={input} onChange={(e) => setInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && addTodo()} /> <button onClick={addTodo}>Ajouter</button> <ul> {todos.map(todo => ( <li key={todo.id}> {todo.text} <button onClick={() => removeTodo(todo.id)}>×</button> </li> ))} </ul> </div> ); }
Points clés à retenir
- useState retourne
[valeur, setValeur] - Ne jamais modifier l'état directement, toujours utiliser le setter
- Spread operator pour les objets et tableaux :
{...obj},[...arr] - Forme fonctionnelle :
setState(prev => ...)quand le nouvel état dépend de l'ancien - L'état est local au composant