Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Quand les effets s'exécutent, le timing du nettoyage et le suivi des dépendances.
useEffect est le hook principal pour synchroniser les composants React avec des systèmes externes — APIs du navigateur, bibliothèques tierces, requêtes réseau et abonnements. Il s'exécute après que le navigateur a dessiné l'écran, le rendant non bloquant. Chaque effet retourne optionnellement une fonction de nettoyage que React appelle avant de ré-exécuter l'effet et lors du démontage du composant.
React commite les mutations DOM, le navigateur dessine l'écran, puis React exécute de manière asynchrone vos callbacks useEffect. Cet ordre signifie que les effets ne bloquent jamais le rendu initial — l'utilisateur voit le contenu immédiatement.
React compare le tableau de deps courant avec celui du rendu précédent en utilisant l'égalité Object.is(). Si une dep a changé, l'effet est planifié pour se ré-exécuter. Pas de tableau de deps = exécution après chaque rendu. Tableau vide [] = exécution une fois après le montage.
Si votre effet retourne une fonction, React la stocke comme nettoyage. Avant de ré-exécuter l'effet (parce qu'une dep a changé), React appelle d'abord le nettoyage précédent. Lors du démontage du composant, React appelle le dernier nettoyage.
En StrictMode, React monte intentionnellement → démonte → remonte chaque composant pour exposer les effets qui ne se nettoient pas correctement. L'invocation double se produit uniquement en développement. C'est pourquoi un nettoyage correct est non négociable.
useLayoutEffect a une sémantique identique à useEffect mais se déclenche de manière synchrone après les mutations DOM et avant le dessin du navigateur. Utilisez-le lorsque votre effet doit lire les mesures DOM et appliquer des corrections avant que l'utilisateur ne voie quoi que ce soit.
Contrôle la fréquence de ré-exécution. [] = montage uniquement. [a, b] = lorsque a ou b change. Pas de tableau = chaque rendu. React compare avec Object.is() — évitez les objets/tableaux comme deps.
La fonction retournée par votre effet. Appelée avant la ré-exécution de l'effet (dep changée) et lors du démontage. Utilisée pour annuler les requêtes, effacer les timers, se désabonner.
Lorsqu'un effet capture le state/les props d'un ancien rendu mais ne se ré-exécute jamais pour obtenir les valeurs mises à jour. Résolu en ajoutant la variable aux deps ou en utilisant une ref.
Se déclenche de manière synchrone après la mutation DOM, avant le dessin. Bloque le dessin — gardez-le rapide. À utiliser pour les mesures DOM (getBoundingClientRect, position de défilement).
Se déclenche avant toute mutation DOM. Prévu pour les bibliothèques CSS-in-JS pour injecter des règles de style de manière synchrone avant la mise en page. Pas pour le code applicatif général.
1// 1. Event subscription — must clean up2useEffect(() => {3 window.addEventListener('resize', handleResize);4 return () => window.removeEventListener('resize', handleResize);5}, []); // mount once, clean up on unmount67// 2. Fetch with cancellation (AbortController)8useEffect(() => {9 const controller = new AbortController();1011 fetch('/api/data', { signal: controller.signal })12 .then(res => res.json())13 .then(setData)14 .catch(err => {15 if (err.name !== 'AbortError') throw err; // ignore cancellation16 });1718 return () => controller.abort(); // cancel if dep changes or unmount19}, [userId]);2021// 3. useLayoutEffect for DOM measurement22useLayoutEffect(() => {23 const height = ref.current.getBoundingClientRect().height;24 setContainerHeight(height); // applied before paint — no flicker25}, [content]);
useEffect est le hook le plus mal utilisé dans React. Bien utiliser les effets — deps corrects, nettoyage approprié, bon timing — est essentiel pour créer des applications sans fuites mémoire, sans requêtes réseau superflues et sans bugs de données périmées. Le changement de modèle mental est : 'les effets sont des mécanismes de synchronisation', pas des 'événements de cycle de vie'. Vous dites à React comment rester synchronisé avec le monde extérieur, pas comment s'accrocher aux callbacks montage/mise à jour/démontage.
A developer writes `useEffect(() => { fetchData() })` without a dependency array. The app makes thousands of API calls per second, the browser freezes, and the API rate limits the user.
No dependency array means the effect runs AFTER EVERY RENDER. fetchData sets state → state change triggers re-render → re-render fires the effect → effect calls fetchData → sets state → infinite loop. Each loop iteration also creates a new network request.
Add the correct dependency array: `useEffect(() => { fetchData() }, [])` for mount-only, or `useEffect(() => { fetchData(query) }, [query])` for re-fetch when query changes. The dependency array tells React's effect scheduler when to skip re-execution.
Takeaway: The dependency array is not optional decoration — it's the core control mechanism of useEffect. No array = every render. Empty array = mount only. Specific deps = run when those values change. Getting this wrong is the #1 source of useEffect bugs.
A countdown timer displays the time remaining. You use `setInterval` inside useEffect with an empty dependency array. The timer shows the same number forever instead of counting down.
The useEffect callback closes over the initial `count` value (e.g., 60). setInterval calls `setCount(count - 1)` every second, but `count` is always 60 inside the closure (captured at mount time). Result: `setCount(59)` every second — never changes from 59.
Use the functional updater form: `setCount(prev => prev - 1)`. The updater receives the CURRENT state, not the stale closure value. Alternatively, store count in a ref and sync it every render. The functional form is React's escape hatch for stale closures in effects.
Takeaway: Effects capture values at the time they're created (closures). For intervals and timeouts that need current state, always use the functional updater `setState(prev => ...)` or a ref pattern to bypass the stale closure.