useEffect è un hook React che consente di eseguire effetti collaterali nei componenti funzionali, sostituendo i metodi del ciclo di vita dei componenti di classe: componentDidMount, componentDidUpdate e componentWillUnmount. Secondo React Documentation (2025), useEffect viene eseguito dopo che React ha confermato le modifiche al DOM, garantendo l'accesso all'albero DOM effettivo. L'hook accetta una funzione di effetto e un array opzionale di dipendenze che controlla la frequenza di esecuzione.
Punti chiave
useEffect è un hook aggiunto in React 16.8 per eseguire effetti collaterali nei componenti funzionali. Gli effetti collaterali sono operazioni non direttamente correlate al rendering dell'interfaccia utente: richieste HTTP ad API, sottoscrizioni a eventi, lavoro con timer, manipolazioni del DOM, registrazione e integrazione con librerie di terze parti.
Prima degli hook, tutte queste operazioni dovevano essere inserite nei metodi del ciclo di vita dei componenti di classe: componentDidMount per l'inizializzazione, componentDidUpdate per reagire ai cambiamenti delle props, componentWillUnmount per la pulizia. useEffect ha unificato i tre scenari in una singola API, dove l'array di dipendenze determina quando l'effetto deve essere eseguito. Questo ha semplificato la logica e ridotto la duplicazione del codice, specialmente negli scenari di sottoscrizione.
Secondo React DevTools Usage Survey (2024), useEffect è il secondo hook più popolare dopo useState, utilizzato nell'89% delle applicazioni React. La maggior parte degli sviluppatori lo utilizza per recuperare dati, sincronizzare con sistemi esterni e gestire le sottoscrizioni agli eventi DOM.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
useEffect esegue la funzione di effetto passata dopo che React ha terminato il rendering e aggiornato il DOM. Questa è una differenza fondamentale rispetto ai calcoli in fase di rendering: l'effetto non blocca il rendering, il che è critico per le prestazioni UX. Se gli effetti venissero eseguiti in modo sincrono, gli utenti vedrebbero un'interfaccia congelata durante il caricamento dei dati.
Il ciclo di vita di un effetto tipico si compone di tre fasi. Al montaggio del componente, React esegue l'effetto. Ad ogni aggiornamento, se almeno una dipendenza dell'array è cambiata, React esegue prima la funzione di pulizia dell'effetto precedente, poi il nuovo effetto. Allo smontaggio del componente, viene eseguita solo la funzione di pulizia.
Secondo React Team — useEffect RFC (2024), l'implementazione interna di useEffect utilizza una coda di effetti collaterali nell'albero fiber. Dopo aver confermato le modifiche (commit phase), React attraversa questa coda e chiama le funzioni di effetto nell'ordine in cui sono state dichiarate nel componente. Ogni nodo fiber memorizza un riferimento all'effetto precedente per una corretta pulizia e riavvio.
| Fase | Azione di React | Quando viene eseguita |
|---|---|---|
| Montaggio | Chiamare la funzione effetto | Dopo il primo rendering |
| Aggiornamento | pulizia → effetto | Quando le dipendenze cambiano |
| Smontaggio | Solo pulizia | Quando il componente viene rimosso |
L'array di dipendenze — il secondo argomento di useEffect — determina quando l'effetto deve essere riavviato. React confronta ogni valore nell'array con il rendering precedente usando Object.is. Se almeno un valore è cambiato, l'effetto viene eseguito di nuovo. Se l'array è vuoto ([]), l'effetto viene eseguito una sola volta dopo il montaggio.
Scegliere le dipendenze corrette è la parte più difficile del lavoro con useEffect. L'array deve includere tutte le variabili e funzioni utilizzate all'interno dell'effetto che possono cambiare tra i rendering. Omettere una dipendenza porta a stale closure — l'effetto vede un valore obsoleto dal rendering precedente. Includere dipendenze non necessarie causa riavvii eccessivi e potenziali bug.
// Le dipendenze controllano quando l'effetto viene rieseguito
useEffect(() => {
document.title = `User: ${user.name}`;
}, [user.name]); // riesegui solo quando user.name cambia
// eslint-disable-next-line react-hooks/exhaustive-deps
// Se ometti una dipendenza, ottieni dati obsoleti
React fornisce eslint-plugin-react-hooks con la regola exhaustive-deps, che verifica automaticamente la completezza dell'array di dipendenze. Secondo Meta Engineering Blog (2024), l'attivazione di questo plugin riduce i bug relativi agli hook del 72%. Si consiglia di correggere tutti gli avvisi exhaustive-deps piuttosto che sopprimerli con un commento, tranne in rari casi con logica personalizzata.
Se non si passa alcun array di dipendenze, useEffect verrà eseguito dopo ogni rendering. Questo può essere utile per la sincronizzazione del DOM o la registrazione, ma il più delle volte è un errore: l'effetto viene eseguito troppo frequentemente, portando a perdita di prestazioni. Nella maggior parte dei casi, si dovrebbe passare un array vuoto (una volta al montaggio) o un array con props/state specifici.
Un array vuoto ([]) significa che l'effetto non dipende da alcun valore e viene eseguito strettamente una volta. Questo è l'equivalente di componentDidMount nei componenti di classe. Tuttavia, bisogna ricordare: se l'effetto utilizza props o stato che non sono elencati nell'array di dipendenze, l'effetto utilizzerà i loro valori iniziali e non vedrà mai gli aggiornamenti. Questo si chiama stale capture ed è spesso fonte di bug difficili da trovare.
| Array di dipendenze | Comportamento | Equivalente in classi |
|---|---|---|
| Nessun argomento | Dopo ogni rendering | componentDidUpdate |
| [] | Una volta al montaggio | componentDidMount |
| [a, b] | Quando a o b cambiano | Analogo di ComponentWillReceiveProps |
| return cleanup | Gestire lo smontaggio | componentWillUnmount |
La funzione di pulizia è una funzione che useEffect può restituire dal suo callback. React la chiama allo smontaggio del componente e prima di ri-eseguire l'effetto quando le dipendenze cambiano. La pulizia è necessaria per annullare sottoscrizioni, timer, richieste e qualsiasi risorsa che deve essere rilasciata.
Un esempio tipico è una sottoscrizione WebSocket. Al montaggio, viene creata una connessione; all'aggiornamento delle dipendenze, viene ricreata (la pulizia chiude la vecchia, l'effetto apre una nuova); allo smontaggio, viene chiusa. Senza pulizia, ogni rimontaggio del componente creerebbe una nuova connessione WebSocket, portando a perdite di memoria e connessioni multiple.
useEffect(() => {
const socket = new WebSocket('wss://api.example.com');
socket.onmessage = event => setData(event.data);
// Funzione di pulizia — viene eseguita allo smontaggio e prima della riesecuzione
return () => {
socket.close();
};
}, []);
Secondo React Documentation (2025), AbortController è l'approccio moderno per annullare le richieste fetch nella pulizia. Se l'effetto effettua una richiesta HTTP e il componente viene smontato prima del suo completamento, la richiesta continua a essere eseguita e setState dopo lo smontaggio causa un errore. Crea un AbortController all'interno dell'effetto e chiama controller.abort() nella pulizia per annullare la richiesta.
L'errore più comune è omettere le dipendenze. Ad esempio, l'effetto utilizza la prop userId, ma l'array di dipendenze è vuoto. Di conseguenza, l'effetto viene eseguito una volta con il valore iniziale di userId e non reagisce mai ai suoi cambiamenti. Lo sviluppatore vede che il componente riceve un nuovo userId, ma i dati non vengono aggiornati. eslint-plugin-react-hooks con la regola exhaustive-deps rileva automaticamente questi bug.
// ❌ Race condition — nessuna cancellazione
useEffect(() => {
fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);
// ✅ Risolto con AbortController
useEffect(() => {
const controller = new AbortController();
fetch(`/api/user/${userId}`, { signal: controller.signal })
.then(res => setUser(res));
return () => controller.abort();
}, [userId]);
Per risolvere il problema del ciclo infinito, evita di inserire in useEffect logica che aggiorna lo stato in base allo stato precedente. Usa la forma funzionale di setState o sposta i calcoli fuori dall'effetto. Se l'effetto si sottoscrive a eventi di storage o del browser, assicurati che l'istanza del listener venga creata una volta, non a ogni rendering.
Domande frequenti
Direttamente — no, perché useEffect si aspetta la restituzione di una funzione sincrona o undefined. Se il callback viene dichiarato come async, restituisce una Promise che React ignora e il meccanismo di pulizia smette di funzionare. Soluzione: chiama una funzione async all'interno dell'effetto: useEffect(() => { async function load() { ... }; load(); }, []).
Non c'è limite. React raccomanda di separare logiche non correlate in useEffect individuali, anche se hanno lo stesso array di dipendenze. Ogni effetto dovrebbe essere responsabile di un compito collaterale chiaramente definito: uno per le sottoscrizioni, un altro per il caricamento dei dati, un terzo per sincronizzare il titolo della scheda. Questo semplifica la comprensione e il debugging.
In React Strict Mode (solo modalità sviluppo), tutti gli effetti vengono montati, smontati e rimontati. Questa è una funzionalità, non un bug — React verifica che la pulizia funzioni correttamente. Se dopo lo smontaggio e il rimontaggio l'effetto si comporta in modo errato (ad esempio, sottoscrizioni duplicate), la tua pulizia è incompleta. In produzione, l'effetto viene eseguito una volta.
Usa AbortController. Crea un controller all'interno dell'effetto, passa controller.signal a fetch e chiama controller.abort() nella pulizia. Se il componente viene smontato prima del completamento della richiesta, fetch viene annullato e setState non verrà chiamato. Questo previene le race condition e l'errore “Can't perform a React state update on an unmounted component”.
useEffect verrà eseguito dopo ogni rendering senza eccezioni. Ciò significa che qualsiasi setState all'interno dell'effetto causerà un nuovo rendering → nuovo effetto → ciclo infinito. In pratica, un effetto senza array di dipendenze è quasi sempre un errore. Le eccezioni sono la registrazione o la sincronizzazione con un sistema esterno dove ogni rendering richiede sincronizzazione.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche