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 :
| Phase | Description |
|---|---|
| Mounting | Le composant est créé et inséré dans le DOM |
| Updating | Les props ou le state changent → re-render |
| Unmounting | Le composant est retiré du DOM |
Méthodes de classe vs Hooks
| Classe (legacy) | Hook équivalent |
|---|---|
constructor() | useState(valeurInitiale) |
componentDidMount() | useEffect(() => { ... }, []) |
componentDidUpdate(prevProps, prevState) | useEffect(() => { ... }, [dep]) |
componentWillUnmount() | useEffect(() => { return () => { ... } }, []) |
shouldComponentUpdate() | React.memo / useMemo |
componentDidCatch() | Error Boundary (classe uniquement) |
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
- La fonction du composant s'exécute (rendu initial)
- React met à jour le DOM
- 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
- Le state ou les props changent
- React re-exécute la fonction du composant
- React met à jour le DOM (diff)
- 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
}, []);