PAS7 Studio

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.

07 mar 2026· 17 min di lettura· Tecnologia
Ideale perIngegneri frontendTech leadSviluppatori React che stanno aggiornando a React 19.2Team che stanno ripulendo gli Effects prima di adottare pattern React più recenti
Cover con taccuino che mostra una pulizia delle dipendenze prima e dopo: dipendenze caotiche dell’Effect a sinistra e una separazione pulita tra Effect ed Effect Event a destra

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?”

Un mental model per dividere un Effect disordinato in un confine di sincronizzazione e in un callback simile a un evento. [1][2][6]
Pattern concreti per subscriptions, DOM listeners, timers, analytics e custom Hooks. [1][4]
Una spiegazione chiara del perché useEffectEvent è migliore dei workaround con useRef nei casi giusti. [1][2]
Una guida compatibile con il linter: quando exhaustive-deps deve ancora guidare il design e quando l’elenco delle dipendenze è troppo ampio perché il codice mescola responsabilità diverse. [1][3]
Dettagli interessanti che i team spesso ignorano, inclusi gli Effect Events nei custom effect hooks e le più recenti shared settings di ESLint per i custom effect hooks. [1][3][7]
Una checklist per aggiornare un codebase reale senza trasformare useEffectEvent in una scorciatoia per l’array delle dipendenze. [1][2][5]

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]

Screenshot della sezione why-this-hook-exists

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-exists

La riformulazione importante

Non è solo uno strumento contro le stale closures. È un modo per dire: “Questo Effect possiede il wiring, e questo callback annidato possiede la reazione a ciò che quel wiring emette.” [1][2]

Se un team deve ricordare una sola sezione di questo articolo, dovrebbe essere questa.

01

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]

02

Mantieni reattiva la sincronizzazione

I valori che definiscono a cosa ci stiamo connettendo, cosa stiamo ascoltando o schedulando restano nell’array delle dipendenze. Se cambiare roomId, serverUrl o element deve riavviare il setup, quelle sono dipendenze reali. [1][2]

03

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]

TSX
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]

Il pattern principale: mantieni la connessione agganciata a roomId e lascia che l’Effect Event legga i valori più recenti senza allargare il confine delle dipendenze. [1][6]

Screenshot della sezione canonical-chat-pattern

Perché questo esempio conta

La soluzione non è “rimuovere dipendenze”. La soluzione è smettere di infilare la logica di risposta nello stesso confine reattivo che possiede il lifecycle della connessione. [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]

TSX
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 subscriptions

Indicazione di design

Un subscription Effect sano ha un elenco di dipendenze piccolo che descrive il target della connessione, mentre il comportamento specifico dell’evento vive dietro un Effect Event e continua a vedere i valori di render più recenti. [1][4]

Il 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]

TSX
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?”.

Regola per gli analytics

Quando il tracking è attivato da una sorgente esterna di eventi, useEffectEvent offre un confine più pulito rispetto sia a dependency arrays troppo ampi sia ai workaround con “latest values in un ref”. [1][2]

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 pointMantieni reattivo nell’EffectSposta in useEffectEvent
IntervalL’avvio e la pulizia del timer, più valori come enabled o delay che devono ricrearloCiò che accade a ogni tick quando quella logica ha bisogno dei props o dello state più recenti
Window listenerL’attacco e la rimozione del listener, insieme al vero event target e al tipo di eventoLa logica dell’handler che ha bisogno di filters, labels, locale o mute flags aggiornati
Observer callbackLa creazione e la disconnessione dell’observer, più il suo target e le observer optionsI metadati analytics più recenti o la reazione della UI quando parte il callback
Third-party SDK subscriptionL’inizializzazione e il teardown dell’SDK, più la resource identity che definisce la subscriptionIl 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-details

Advanced takeaway

L’uso più maturo di useEffectEvent emerge nei custom Hooks, in setup di lint disciplinati e in code review che per prima cosa chiedono se quell’Effect sia davvero necessario. [1][3][4][5]

Il 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]

Sostituire ciecamente tutti i pattern basati su ref. Un ref ha ancora casi d’uso validi; useEffectEvent è pensato specificamente per logica simile a un evento attivata da Effects. [1][2]

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

È la scelta migliore quando un sistema esterno deve restare sincronizzato con un set ristretto di dipendenze, ma il callback che quel sistema attiva ha bisogno dei render values più recenti. Ottimo per subscriptions, listeners, timers e analytics. [1][2]

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]

La vera regola di decisione

La domanda più difficile ma più utile non è “come faccio a mantenere aggiornato questo callback?”, ma “chi possiede davvero la sincronizzazione qui?”. [1][2][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.

Separa i “latest context values” dai “connection identity values”.

Theme, locale, plan, experiment, mute flags, route labels e analytics metadata sono esempi tipici. [1][2]

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]

Rivedi insieme custom Hooks e impostazioni ESLint.

Se il team usa wrapper hooks intorno agli Effects, rivedi la configurazione additionalEffectHooks nel React hooks ESLint plugin. [3][7]

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.

Quale problema risolve davvero `useEffectEvent`?

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]

`useEffectEvent` sostituisce i dependency arrays?

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]

`useEffectEvent` è meglio di `useRef`?

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]

Posso usare `useEffectEvent` dentro i custom Hooks?

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]

Posso passare un Effect Event a un altro componente?

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]

Qual è l’errore più grande durante il rollout?

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]

ESLint capisce i custom effect hooks in questo scenario?

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.

Verificato: 08 mar 2026Valido per: React 19.2+Valido per: App React con subscriptions, listeners, timers e Effects per analyticsTestato con: documentazione ufficiale React 19.2Testato con: reference di useEffectEventTestato con: guida eslint-plugin-react-hooksTestato con: guida custom Hooks

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.

Sei qui04/04

useEffectEvent in profondit?: design degli Effect, subscriptions e analytics

Precedente
Successivo

Articoli correlati

growth

AI SEO / GEO nel 2026: i tuoi prossimi clienti non sono umani — sono agenti

La ricerca sta passando dai click alle risposte. Bot e agenti AI scansionano, citano, raccomandano e sempre più spesso acquistano. Scopri cosa significa AI SEO / GEO, perché la SEO classica non basta più e come PAS7 Studio aiuta i brand a vincere visibilità nel web “agentico”.

blogs

Il chip Apple più potente? M5 Pro e M5 Max battono i record

Analisi di Apple M5 Pro e M5 Max aggiornata a marzo 2026. Spieghiamo perché questi chip possono essere considerati i SoC professionali per notebook più potenti di Apple, come si posizionano contro M4 Pro, M4 Max, M1 Pro, M1 Max e cosa mostrano rispetto ai concorrenti Intel e AMD.

telegram-media-saver

Tag automatici e ricerca per link salvati

Integra con GDrive/S3/Notion per tag automatici e ricerca veloce tramite API di ricerca

services

Sviluppo di bot e servizi di automazione

Sviluppo professionale di bot Telegram e automazione dei processi aziendali: chatbot, assistenti AI, integrazioni CRM, automazione dei flussi di lavoro.

Sviluppo professionale per la tua attività

Creiamo soluzioni web moderne e bot per le aziende. Scopri come possiamo aiutarti a raggiungere i tuoi obiettivi.