En JavaScript classique, vous pouvez modifier une variable :
let count = 0; count = count + 1;
Mais en React, cela ne fonctionne pas :
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.
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 :
count)setCount)const [state, setState] = useState(initialValue);
state : la valeur actuellesetState : fonction pour modifier la valeurinitialValue : valeur initiale (une seule fois au premier rendu)const [count, setCount] = useState(0); setCount(count + 1); setCount(count - 1);
const [name, setName] = useState(''); setName('Jean'); setName(e.target.value); // Pour un input
const [isOpen, setIsOpen] = useState(false); setIsOpen(true); setIsOpen(!isOpen); // Toggle
const [user, setUser] = useState({ name: '', email: '' }); // INCORRECT - mutation directe user.name = 'Jean'; // Ne fonctionne pas ! // CORRECT - nouvel objet avec spread setUser({ ...user, name: 'Jean' });
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 ));
Quand la nouvelle valeur dépend de l'ancienne, utilisez la forme fonctionnelle :
// 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.
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 :
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'); }
Si la valeur initiale nécessite un calcul coûteux, passez une fonction :
// PROBLÈME - calcul exécuté à chaque rendu const [data, setData] = useState(expensiveCalculation()); // CORRECT - calcul exécuté une seule fois const [data, setData] = useState(() => expensiveCalculation());
| 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 |
Créer un compteur qui :
Solution :
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> ); }
Créer un composant qui :
Solution :
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> ); }
[valeur, setValeur]{...obj}, [...arr]setState(prev => ...) quand le nouvel état dépend de l'ancien