Effect (useEffect)
Gérer les side-effects et le monde extérieur
Le concept : Les composants React doivent idéalement être "purs" (ils ne modifient rien en dehors de leur rendu visuel).
Lorsqu'il faut interagir avec le monde extérieur (récupérer des données API, s'abonner à WebSocket, démarrer un timer), nous utilisons les Side-Effects (effets secondaires) grâce au hook useEffect. C'est le portail contrôlé de votre composant vers l'extérieur.
💡 Effets de bord & cycle de vie
Les composants React doivent être purs (mêmes entrées → même sortie). Mais parfois, il faut interagir avec le monde extérieur : appel API, abonnement WebSocket, modification du titre de la page, timer... C'est ce qu'on appelle des effets de bord.
useEffect est le "sas de sécurité" qui isole ce code. Il s'exécute après que React a rendu le composant dans le DOM.
📌 Syntaxe et exemples fondamentaux
import { useEffect } from 'react'; // 1. Une seule fois (montage) useEffect(() => { document.title = "Bonjour !"; }, []); // 2. À chaque changement de `query` useEffect(() => { search(query); }, [query]); // 3. Avec cleanup (nettoyage) useEffect(() => { const handler = () => console.log("scroll"); window.addEventListener("scroll", handler); return () => window.removeEventListener("scroll", handler); }, []);
🌐 Cas d'usage réels
function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let ignore = false; // évite les race conditions setLoading(true); fetch(`/api/users/${userId}`) .then(r => r.json()) .then(data => { if (!ignore) { setUser(data); setLoading(false); } }); return () => { ignore = true; }; }, [userId]); // refetch si userId change if (loading) return <Spinner />; return <h1>{user.name}</h1>; }
// Timer avec cleanup useEffect(() => { const id = setInterval(() => { setTime(t => t + 1); }, 1000); return () => clearInterval(id); }, []); // Sync avec localStorage useEffect(() => { localStorage.setItem('theme', theme); }, [theme]); // Connexion WebSocket useEffect(() => { const ws = new WebSocket(url); ws.onmessage = (e) => setMessages(m => [...m, e.data]); return () => ws.close(); // Fermer à la déconnexion }, [url]);
⚠️ Pièges courants
// ❌ PIÈGE : "stale closure" (valeur périmée) useEffect(() => { console.log(count); // count figé à sa valeur initiale ! }, []); // count est utilisé mais absent du tableau // ✅ CORRECT useEffect(() => { console.log(count); }, [count]); // count est dans le tableau
// ❌ useEffect ne peut pas être async directement useEffect(async () => { const data = await fetch("/api"); // 🚨 Retourne une Promise, pas une cleanup fn }, []); // ✅ Définir une function async DANS l'effet useEffect(() => { async function load() { const data = await fetch("/api"); setData(await data.json()); } load(); }, []);
useEffect. S'exécute avant que le navigateur repeigne l'écran. Utile pour lire/modifier des positions DOM sans flash visuel (ex: faire apparaître un tooltip au bon endroit). À utiliser rarement, préférez useEffect.