# Chapitre 1 : useState
## Le problème
En JavaScript classique, vous pouvez modifier une variable :
```javascript
let count = 0;
count = count + 1;
```
Mais en React, cela ne fonctionne pas :
```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 (
{count}
{/* Affiche toujours 0 */}
);
}
```
**Pourquoi ?** React ne sait pas que la variable a changé. Il faut lui dire explicitement.
## La solution : useState
```javascript
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1); // Dit à React de re-rendre
};
return (
{count}
);
}
```
`useState` retourne un tableau avec :
1. **La valeur actuelle** (`count`)
2. **Une fonction pour la modifier** (`setCount`)
## Syntaxe
```javascript
const [state, setState] = useState(initialValue);
```
- `state` : la valeur actuelle
- `setState` : fonction pour modifier la valeur
- `initialValue` : valeur initiale (une seule fois au premier rendu)
## Différents types d'état
### Nombres
```javascript
const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count - 1);
```
### Chaînes
```javascript
const [name, setName] = useState('');
setName('Jean');
setName(e.target.value); // Pour un input
```
### Booléens
```javascript
const [isOpen, setIsOpen] = useState(false);
setIsOpen(true);
setIsOpen(!isOpen); // Toggle
```
### Objets
```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
```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 :
```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
```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 :
```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 :
```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 :**
```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 (
{count}
);
}
```
### 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 :**
```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 (
);
}
```
## Points clés à retenir
1. **useState** retourne `[valeur, setValeur]`
2. **Ne jamais modifier** l'état directement, toujours utiliser le setter
3. **Spread operator** pour les objets et tableaux : `{...obj}`, `[...arr]`
4. **Forme fonctionnelle** : `setState(prev => ...)` quand le nouvel état dépend de l'ancien
5. L'état est **local** au composant
[[:15_training:module3-hooks-fondamentaux:start|← Retour au module]] | [[:15_training:module3-hooks-fondamentaux:useeffect|Chapitre suivant : useEffect →]]