React useEffectEvent in profondità: stale closures, sottoscrizioni, listener, timer e analytics in React 19.2
Un deep dive pratico su React useEffectEvent per React 19.2: come correggere le stale closures, ridurre il reconnect churn nelle sottoscrizioni, mantenere listener e timer aggiornati e progettare Effects per analytics più puliti senza ref hack o soppressione delle dipendenze.

Guida React 2026 Primitives & Compiler Upgrade
Questo capitolo si concentra su React `useEffectEvent`: correzione delle stale closures, design degli Effect, confini delle subscriptions, pattern per listeners e timers, custom Hooks e callback di analytics che restano aggiornati senza reconnect churn.
Tutti gli articoli di questa guida
01
Panoramica: React 2026 primitives e mental model dell?era compiler
Il quadro architetturale completo: cosa ? cambiato e dove ogni primitive trova davvero posto.
02
useActionState in profondit?: mutation flows, optimistic UI e integration patterns
Quando useActionState riduce il boilerplate e quando un data layer resta il vero owner del problema.
03
React <Activity />: mantieni lo state, metti in pausa gli Effects e renderizza in background
Pattern reali, trade-off di performance e pitfalls per tabs, drawers e shell UI.
04
useEffectEvent in profondit?: design degli Effect, subscriptions e analytics
Confini degli Effect compatibili con il linter senza stale closures o reconnect churn.
cosa otterrai
Questo capitolo parla di design degli Effect, non di curiosità sui Hook. La domanda non è “dove posso usare useEffectEvent?”, ma “quali parti di questo Effect sono davvero reattive e quali sono solo risposte a eventi che hanno bisogno di valori aggiornati?”
Il bug da stale closure è il sintomo che tutti notano subito: un callback dentro un Effect legge valori vecchi. Ma il problema più profondo è architetturale. I team spesso comprimono due lavori diversi dentro un solo Effect.
Il primo lavoro è la sincronizzazione: connettersi a una chat room, agganciare un listener, avviare un timer, sottoscriversi a un SDK o inizializzare una browser API. Il secondo è la logica di risposta: mostrare una notifica con il tema corrente, includere i metadati analytics più recenti, controllare un mute flag o formattare dati per la locale corrente. Questi due lavori invecchiano in modo diverso e non dovrebbero sempre reagire allo stesso set di dipendenze. [1][2][5]
Prima di useEffectEvent, i team gestivano questa tensione con dependency arrays troppo ampi che causavano reconnect churn oppure con refs mutabili che risolvevano le stale reads ma rendevano il design più difficile da capire in code review. useEffectEvent esiste per rendere esplicita questa separazione. [1][2]
React 19.2 introduce useEffectEvent come una delle funzionalità principali del rilascio. [2]
La guida di base di React resta valida: gli Effects servono per sincronizzarsi con sistemi esterni, non per ogni cambiamento di state o user action. [5]
Screenshot della sezione why-this-hook-existsSe un team deve ricordare una sola sezione di questo articolo, dovrebbe essere questa.
Trova il sistema esterno
Un Effect dovrebbe sincronizzarsi con qualcosa fuori da React: una subscription, un timer, un DOM listener, una browser API o un SDK. Se non esiste un sistema esterno, la documentazione React dice che probabilmente non ti serve un Effect. [5]
Sposta la logica di risposta simile a un evento in useEffectEvent
Quando il sistema esterno emette un evento, potresti aver bisogno del tema, della locale, del piano, del mute flag, del contesto analytics o delle regole di formattazione più recenti. Se quei valori non dovrebbero riavviare la sincronizzazione stessa, quella logica è una buona candidata per useEffectEvent. [1][2]
Un test semplice
Chiediti: “Se questo valore cambia, devo riconnettermi o riattaccare il listener?” Se sì, tienilo come dipendenza. Se no, ma il callback ha comunque bisogno del valore più recente quando l’evento scatta, è lì che useEffectEvent inizia ad avere senso. [1]
Le spiegazioni ufficiali di React tornano spesso agli esempi di chat room perché mostrano il problema immediatamente. La connessione deve essere reattiva a roomId. La notifica deve comunque vedere il tema più recente o altro UI state. Sono due responsabilità diverse.
Quando le modelli separatamente, il confine dell’effect diventa più facile da rivedere: l’Effect possiede setup e teardown della connessione, mentre l’Effect Event possiede la logica di cosa fare quando arriva un evento dalla connessione. [1][2]
import { useEffect, useEffectEvent } from "react";
type Props = {
roomId: string;
theme: "light" | "dark";
};
declare function createConnection(roomId: string): {
on(event: "connected", cb: () => void): void;
connect(): void;
disconnect(): void;
};
declare function showNotification(message: string, theme: Props["theme"]): void;
export function ChatRoom({ roomId, theme }: Props) {
const onConnected = useEffectEvent(() => {
showNotification("Connected!", theme);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on("connected", () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]);
return null;
}Questo è il momento “aha” più pulito dell’API: il valore più recente e la dipendenza reattiva non sono sempre la stessa cosa. [1][2]
È nelle subscriptions che questo pattern ripaga subito. Chat, metriche live, billing streams, listener in stile Firebase, media APIs e SDK di terze parti condividono lo stesso failure mode: il setup dovrebbe dipendere da un set ridotto di valori, mentre il callback spesso vuole un set più ampio di valori “latest” della UI.
Un odore tipico è un dependency array che cresce a ogni sprint: roomId, serverUrl, theme, muted, locale, trackingContext, featureFlags, plan, segment. A quel punto l’Effect non descrive più l’external synchronization boundary; descrive tutto ciò che è stato semplicemente referenziato dentro un callback annidato.
React mostra anche che useEffectEvent è particolarmente efficace quando costruisci hook riutilizzabili. Un custom hook può avvolgere un callback ricevuto con useEffectEvent, così il hook resta sottoscritto solo ai veri connection inputs e non forza una reconnessione ogni volta che il parent rerenderizza con una nuova callback identity. [1][4]
import { useEffect, useEffectEvent } from "react";
type Message = { id: string; body: string };
type Options = {
serverUrl: string;
roomId: string;
muted: boolean;
locale: string;
onReceiveMessage: (message: Message) => void;
};
declare function createConnection(options: {
serverUrl: string;
roomId: string;
}): {
connect(): void;
disconnect(): void;
on(event: "message", cb: (message: Message) => void): void;
};
declare function playSound(name: "message"): void;
declare function formatMessagePreview(message: Message, locale: string): Message;
export function useChatRoom({
serverUrl,
roomId,
muted,
locale,
onReceiveMessage,
}: Options): void {
const onMessage = useEffectEvent((message: Message) => {
const nextMessage = formatMessagePreview(message, locale);
if (!muted) {
playSound("message");
}
onReceiveMessage(nextMessage);
});
useEffect(() => {
const connection = createConnection({ serverUrl, roomId });
connection.connect();
connection.on("message", (message) => {
onMessage(message);
});
return () => connection.disconnect();
}, [serverUrl, roomId]);
}La guida React sui custom Hooks mostra uno dei casi production più utili: mantieni la subscription agganciata ai reali connection inputs, non alla callback identity. [4]
Screenshot della sezione subscriptionsIl codice analytics è il punto in cui i team vogliono più spesso due cose contraddittorie allo stesso tempo: subscriptions stabili e contesto fresco.
Immagina un listener di un video player, un billing event stream, un observer callback o un websocket event. La subscription vera e propria dovrebbe dipendere dall’identità della risorsa: il video, l’account, l’observer target o il canale. Ma il tracking payload emesso dovrebbe includere il piano, l’experiment bucket, la locale, i route metadata o il nome del product surface più recenti. Quelli sono contesto corrente, non identità della connessione.
È proprio qui che useEffectEvent è più facile da leggere di un pattern tipo ref.current = latestStuff. Un ref può funzionare, ma nasconde l’intento. Un Effect Event dice chiaramente, nel codice: “questo callback fa parte della storia dell’Effect, ma non è una synchronization dependency.” [1][2]
import { useEffect, useEffectEvent } from "react";
type Props = {
accountId: string;
plan: "free" | "pro" | "team";
experiment: string;
routeName: string;
};
declare function subscribeToBillingEvents(
accountId: string,
onEvent: (eventName: string) => void,
): () => void;
declare function track(event: string, props: Record<string, string>): void;
export function BillingAnalytics({
accountId,
plan,
experiment,
routeName,
}: Props) {
const onBillingEvent = useEffectEvent((eventName: string) => {
track("billing_event", {
eventName,
plan,
experiment,
routeName,
});
});
useEffect(() => {
return subscribeToBillingEvents(accountId, (eventName) => {
onBillingEvent(eventName);
});
}, [accountId]);
return null;
}Questo pattern è particolarmente efficace nel product analytics perché separa chiaramente “a cosa siamo collegati?” da “con quali dati dovremmo etichettare questo evento proprio adesso?”.
Timers, listeners, observers e callback di SDK pongono la stessa domanda: quali valori devono governare setup/cleanup e quali devono essere letti solo quando succede qualcosa? [1]
| Comparison point | Mantieni reattivo nell’Effect | Sposta in useEffectEvent |
|---|---|---|
| Interval | L’avvio e la pulizia del timer, più valori come enabled o delay che devono ricrearlo | Ciò che accade a ogni tick quando quella logica ha bisogno dei props o dello state più recenti |
| Window listener | L’attacco e la rimozione del listener, insieme al vero event target e al tipo di evento | La logica dell’handler che ha bisogno di filters, labels, locale o mute flags aggiornati |
| Observer callback | La creazione e la disconnessione dell’observer, più il suo target e le observer options | I metadati analytics più recenti o la reazione della UI quando parte il callback |
| Third-party SDK subscription | L’inizializzazione e il teardown dell’SDK, più la resource identity che definisce la subscription | Il contesto UI più recente o la formattazione dell’evento usata quando l’SDK emette qualcosa |
Lettura pratica
Lascia all’Effect il wiring. Lascia a useEffectEvent la risposta all’evento che deve restare aggiornata senza allargare il synchronization boundary. [1]
La documentazione contiene alcuni dettagli facili da saltare, ma importanti nei codebase più grandi.
Quest’ultimo punto merita enfasi, perché useEffectEvent è facile da applicare troppo. Migliora il design degli Effect, ma non giustifica l’esistenza di un Effect che non avrebbe mai dovuto esserci.
supportati ufficialmente
React supporta esplicitamente l’uso di useEffectEvent dentro i propri custom Hooks. Per API riutilizzabili di subscriptions e listeners è una differenza importante. [1][4]
tienilo locale
Gli Effect Events devono essere chiamati solo da Effects o da altri Effect Events nello stesso componente o hook. Non passarli in giro come normali callback props. [1]
custom effect hooks
Il plugin React hooks documenta le shared settings additionalEffectHooks per custom effect hooks, aiutando i team a lintare i wrapper hooks in modo coerente. [3][7]
spesso è la soluzione migliore
La guida React “You Might Not Need an Effect” resta valida. A volte il refactor giusto non è useEffectEvent, ma spostare del tutto la logica fuori da un Effect inutile. [5]
La pagina di reference definisce chiaramente i confini: gli Effect Events sono locali agli Effects, sono intenzionalmente esclusi dai dependency arrays e non sono un primitive di callback generico. [1]
Screenshot della sezione interesting-detailsIl Hook è piccolo. La maggior parte degli errori arriva quando lo si usa come scorciatoia e non come strumento di design.
Usare useEffectEvent per nascondere un valore che dovrebbe davvero riavviare l’Effect. Se cambiare quel valore dovrebbe risottoscrivere, riattaccare o riconnettere, allora è ancora una dipendenza. [1]
Trattare gli Effect Events come normali event handlers o passarli tramite props. La reference React è esplicita: vanno chiamati da Effects o da altri Effect Events, non dalla render logic o da confini arbitrari di componenti. [1]
Lasciare giganteschi Effects e spostare righe casuali dentro un Effect Event finché il linter smette di protestare. Non è un refactor; è dependency laundering.
Saltare la domanda “mi serve davvero questo Effect?”. La documentazione React continua a consigliare di eliminare gli Effects inutili invece di decorarli con nuove API. [5]
Il controllo più semplice
Se dopo il refactor non riesci a spiegare il synchronization boundary in una frase, probabilmente l’Effect sta ancora facendo troppe cose.
Queste scelte risolvono problemi diversi. Trattarle come equivalenti crea codice Effect disordinato.
Usa useEffectEvent
Mantieni dipendenze più ampie
È corretto quando il valore cambiato altera davvero il synchronization boundary. Una reconnessione non è un bug se la connessione è realmente cambiata. [1]
Usa un ref
Resta un escape hatch valido per alcuni scenari legati alla mutabilità, ma per la callback logic di tipo evento è di solito meno esplicativo di un Effect Event. Preferiscilo quando il problema è la mutabilità, non il design dell’effect event. [1]
Elimina l’Effect
È la soluzione migliore quando non esiste un sistema esterno e la logica dovrebbe vivere nel codice di render, in derived state o in un normale event handler. Spesso è anche la correzione più pulita e rapida. [5]
Usala durante una cleanup pass su React 19.2 o in review di custom Hooks che avvolgono subscriptions e listeners.
Fai audit di ogni Effect che tocca un sistema esterno.
Inizia da sockets, listeners, timers, observers, media APIs e SDK wrappers. Sono i candidati con più valore da rivedere. [5]
Scrivi il vero synchronization boundary.
Nomina i valori che devono davvero causare un nuovo setup e cleanup.
Usa useEffectEvent solo per callback logic di tipo evento.
Se la logica non è attivata da un Effect o da un altro Effect Event, probabilmente dovrebbe stare altrove. [1]
Elimina gli Effects che non sincronizzano con sistemi esterni.
Non trasformare Effects inutili in Effects “furbi”. Rimuovili. [5]
Testa sia il listener churn sia la freshness dei callback.
L’obiettivo è avere meno teardown inutili e un comportamento corretto dei latest values quando l’evento si verifica davvero.
Risultato atteso
Dopo un buon rollout, gli Effects diventano più facili da spiegare, più facili da lintare e meno inclini a riconnettersi per via di presentational state irrilevante.
Permette alla logica simile a un evento, attivata da un Effect, di leggere gli ultimi props e state senza costringere l’Effect circostante a risincronizzarsi. È particolarmente utile per subscriptions, listeners, timers e callback di analytics. [1][2]
No. I valori che definiscono davvero il synchronization boundary devono restare nel dependency array. `useEffectEvent` aiuta quando la callback logic ha bisogno dei valori più recenti ma quei valori non dovrebbero riavviare la subscription o il listener stesso. [1]
Non sempre. Un ref ha ancora casi d’uso validi come escape hatch. Ma per la logica simile a un evento attivata dagli Effects, `useEffectEvent` di solito comunica meglio l’intento e si adatta meglio al modello ufficiale degli Effects in React. [1][2]
Sì. React documenta esplicitamente questo pattern, ed è uno dei casi production più utili perché consente ai hook riutilizzabili di mantenere stabili le subscriptions pur chiamando sempre il callback più recente del consumer. [1][4]
No. Gli Effect Events devono essere chiamati da Effects o da altri Effect Events nello stesso componente o hook, non passati in giro come normali callback props. [1]
Usare `useEffectEvent` come scorciatoia per le dipendenze invece di chiedersi cosa definisce davvero il synchronization boundary. Il secondo errore più comune è non eliminare prima gli Effects inutili. [1][5]
Sì, con la configurazione corretta. Il React hooks plugin documenta le shared settings `additionalEffectHooks` per poter lintare i custom effect hooks in modo coerente. [3][7]
Includiamo solo fonti che supportano direttamente consigli, caveat ed esempi usati in questo capitolo.
Gran parte del dolore negli upgrade React non riguarda la sintassi. Riguarda il debito di design nascosto negli Effects: responsabilità mischiate, dipendenze rumorose, reconnect churn e analytics incollata al layer sbagliato.
PAS7 Studio può aiutarti a fare audit di questi confini, capire quali Effects vanno eliminati e quali riprogettati, e trasformare un upgrade a React 19.2 in un piano di migrazione con veri guardrail ingegneristici.
useEffectEvent in profondit?: design degli Effect, subscriptions e analytics
Sei qui: 04/04
useEffectEvent in profondit?: design degli Effect, subscriptions e analytics