Chapitre 1 : useState
Le problème
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.
La solution : useState
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
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
const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count - 1);
Chaînes
const [name, setName] = useState('');
setName('Jean');
setName(e.target.value); // Pour un input
Booléens
const [isOpen, setIsOpen] = useState(false);
setIsOpen(true);
setIsOpen(!isOpen); // Toggle
Objets
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
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 :
// 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
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');
}
Initialisation paresseuse
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());
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 :
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 :
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