---
title: "Chapitre 1 : useState"
weight: 400
---

# 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 (
        <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

```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 :

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 (
        <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 :**

```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

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

[← Retour au module](/training/module3-hooks-fondamentaux) | [Chapitre suivant : useEffect ->](/training/module3-hooks-fondamentaux/useeffect)
