# Cycle de vie des composants

Mounting, updating, unmounting et équivalents avec les Hooks

# Vue d'ensemble

## Cycle de vie d'un composant React

Chaque composant React passe par trois grandes phases :

<table id="bkmrk-phasedescription-mou"> <thead><tr><th>Phase</th><th>Description</th></tr></thead> <tbody> <tr><td>**Mounting**</td><td>Le composant est créé et inséré dans le DOM</td></tr> <tr><td>**Updating**</td><td>Les props ou le state changent → re-render</td></tr> <tr><td>**Unmounting**</td><td>Le composant est retiré du DOM</td></tr> </tbody></table>

### Méthodes de classe vs Hooks

<table id="bkmrk-classe-%28legacy%29hook-"> <thead><tr><th>Classe (legacy)</th><th>Hook équivalent</th></tr></thead> <tbody> <tr><td>`constructor()`</td><td>`useState(valeurInitiale)`</td></tr> <tr><td>`componentDidMount()`</td><td>`useEffect(() => { ... }, [])`</td></tr> <tr><td>`componentDidUpdate(prevProps, prevState)`</td><td>`useEffect(() => { ... }, [dep])`</td></tr> <tr><td>`componentWillUnmount()`</td><td>`useEffect(() => { return () => { ... } }, [])`</td></tr> <tr><td>`shouldComponentUpdate()`</td><td>`React.memo` / `useMemo`</td></tr> <tr><td>`componentDidCatch()`</td><td>Error Boundary (classe uniquement)</td></tr> </tbody></table>

# Mounting

## Phase de Mounting

Le composant est initialisé et rendu pour la première fois dans le DOM.

### Avec les classes

```
class MonComposant extends Component {
  constructor(props) {
    super(props);
    this.state = { data: null }; // 1. Initialisation du state
  }

  componentDidMount() {
    // 2. Appelé APRÈS le premier render (DOM disponible)
    fetch('/api/data')
      .then(res => res.json())
      .then(data => this.setState({ data }));
  }

  render() {
    return <div>{this.state.data}</div>;
  }
}
```

### Avec les Hooks

```
function MonComposant() {
  const [data, setData] = useState(null); // Initialisation

  useEffect(() => {
    // Équivalent de componentDidMount (tableau vide = exécution unique)
    fetch('/api/data')
      .then(res => res.json())
      .then(data => setData(data));
  }, []); // [] = seulement au montage

  return <div>{data}</div>;
}
```

### Ordre d'exécution au montage

1. La fonction du composant s'exécute (rendu initial)
2. React met à jour le DOM
3. Les effets (`useEffect`) s'exécutent

# Updating

## Phase de Updating

Le composant se re-rend quand ses props ou son state changent.

### Avec les classes

```
componentDidUpdate(prevProps, prevState) {
  // Appelé après chaque re-render
  if (prevProps.userId !== this.props.userId) {
    // Les props ont changé → réagir
    this.fetchUser(this.props.userId);
  }

  if (prevState.count !== this.state.count) {
    // Le state a changé → réagir
    console.log("Count changé :", this.state.count);
  }
}
```

### Avec les Hooks

```
// Réagir aux changements de props ou state
useEffect(() => {
  fetchUser(userId);
}, [userId]); // S'exécute quand userId change

useEffect(() => {
  console.log("Count changé :", count);
}, [count]);
```

### Optimiser les re-renders

```
// Éviter les re-renders inutiles du composant enfant
const EnfantMemo = React.memo(function Enfant({ nom }) {
  return <p>{nom}</p>;
});

// shouldComponentUpdate (classe)
shouldComponentUpdate(nextProps, nextState) {
  return nextProps.value !== this.props.value;
}
```

### Ordre d'exécution lors d'une mise à jour

1. Le state ou les props changent
2. React re-exécute la fonction du composant
3. React met à jour le DOM (diff)
4. Les effets dont les dépendances ont changé s'exécutent (cleanup de l'ancien effet d'abord)

# Unmounting

## Phase de Unmounting

Le composant est retiré du DOM. C'est le moment de nettoyer les effets de bord : timers, abonnements, listeners...

### Avec les classes

```
class Timer extends Component {
  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState(prev => ({ count: prev.count + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    // Nettoyage obligatoire pour éviter les memory leaks
    clearInterval(this.interval);
  }

  render() { return <p>{this.state.count}</p>; }
}
```

### Avec les Hooks (fonction de cleanup)

```
function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);

    // La fonction retournée = cleanup (unmount OU avant le prochain effet)
    return () => clearInterval(interval);
  }, []);

  return <p>{count}</p>;
}
```

### Cas d'usage typiques du cleanup

- Annuler des requêtes HTTP (`AbortController`)
- Désabonner d'un WebSocket / EventEmitter
- Retirer des event listeners (`removeEventListener`)
- Effacer des timers (`clearTimeout`, `clearInterval`)

```
useEffect(() => {
  const controller = new AbortController();

  fetch('/api/data', { signal: controller.signal })
    .then(res => res.json())
    .then(setData)
    .catch(err => {
      if (err.name !== 'AbortError') console.error(err);
    });

  return () => controller.abort(); // Annule la requête si démontage
}, []);
```