# React 19 useEffectEvent e Activity: Nuove API e Domande da Colloquio 2026 > Guida approfondita a useEffectEvent e al componente Activity di React 19.2 – con esempi di codice, best practice e domande frequenti nei colloqui tecnici 2026. - Published: 2026-06-03 - Updated: 2026-06-03 - Author: SharpSkill - Tags: react, react-19, useEffectEvent, activity, colloquio - Reading time: 9 min --- React 19.2 ha introdotto due API che affrontano problemi di vecchia data: `useEffectEvent` elimina le stale closure negli effect, mentre `` consente il pre-rendering in background con preservazione completa dello state. Entrambe le API sono state rilasciate a [ottobre 2025](https://react.dev/blog/2025/10/01/react-19-2) e stanno già modificando il modo in cui le applicazioni React in produzione gestiscono side effect e navigazione. > **Requisito minimo: React 19.2** > > useEffectEvent e Activity richiedono React 19.2 o versione successiva. L'aggiornamento si esegue con `npm install react@latest react-dom@latest`. Il plugin ESLint `eslint-plugin-react-hooks@6+` aggiunge supporto nativo per useEffectEvent nei dependency array. ## Il problema delle stale closure: perch\u00e9 serve useEffectEvent Ogni sviluppatore React ha incontrato almeno una volta il problema delle stale closure. Un effect cattura un valore al momento del rendering e, quando quel valore cambia, l'effect continua a fare riferimento a quello vecchio. La soluzione classica prevedeva l'utilizzo di `useRef` per mantenere un riferimento mutabile: funzionale, ma verboso e invisibile al linter. Si consideri un'applicazione di chat che registra analytics all'arrivo di un nuovo messaggio. Il log deve includere il `theme` corrente, ma i cambiamenti del tema non devono riconnettere la chat: ```tsx // hooks/useChatRoom.ts // Prima di useEffectEvent: workaround con useRef import { useEffect, useRef } from 'react' export function useChatRoom(roomId: string, theme: string) { // Salvare il theme in un ref per evitare la stale closure const themeRef = useRef(theme) themeRef.current = theme useEffect(() => { const connection = createConnection(roomId) connection.on('message', (msg) => { // themeRef.current ha sempre il valore aggiornato logAnalytics('new_message', { roomId, theme: themeRef.current }) showNotification(msg) }) connection.connect() return () => connection.disconnect() }, [roomId]) // theme escluso intenzionalmente — ma il linter avvisa } ``` Questo approccio funziona, ma il linter segnala `theme` come dipendenza mancante. Sopprimere l'avviso nasconde potenziali bug altrove. Il pattern `useRef` oscura anche l'intento: chi legge il codice per la prima volta deve ricostruire mentalmente il motivo per cui `theme` si trova in un ref invece che nel dependency array. ## useEffectEvent: separare la logica reattiva da quella non reattiva L'hook `useEffectEvent` crea una funzione stabile che legge sempre le props e lo state pi\u00f9 recenti, senza attivare la risincronizzazione dell'effect. Sostituisce il pattern `useRef` con un'API dichiarativa che il linter comprende nativamente. ```tsx // hooks/useChatRoom.ts // Dopo useEffectEvent: separazione netta delle responsabilit\u00e0 import { useEffect, useEffectEvent } from 'react' export function useChatRoom(roomId: string, theme: string) { // Effect Event: legge sempre il theme corrente, non provoca reconnect const onMessage = useEffectEvent((msg: string) => { logAnalytics('new_message', { roomId, theme }) showNotification(msg) }) useEffect(() => { const connection = createConnection(roomId) connection.on('message', onMessage) connection.connect() return () => connection.disconnect() }, [roomId]) // Nessun avviso del linter — onMessage \u00e8 un Effect Event } ``` L'effect viene rieseguito solo quando `roomId` cambia. Il callback `onMessage` vede sempre il `theme` corrente senza essere elencato come dipendenza. La [documentazione ufficiale di React](https://react.dev/reference/react/useEffectEvent) stabilisce esplicitamente che gli Effect Event devono essere chiamati solo dall'interno degli effect, mai durante il rendering o passati ai componenti figli. ## Regole e vincoli di useEffectEvent Tre regole governano `useEffectEvent`: 1. Va chiamato al livello superiore di un componente o di un custom hook, mai all'interno di loop o condizioni 2. La funzione restituita pu\u00f2 essere chiamata solo all'interno di `useEffect` o di un altro Effect Event 3. Non deve essere passata come prop n\u00e9 restituita da un hook per uso esterno Violare la regola 2 causa bug sottili: l'identit\u00e0 della funzione cambia a ogni render, quindi salvarla in un ref o passarla verso il basso vanifica lo scopo. Il plugin `eslint-plugin-react-hooks@6+` [applica questi vincoli](https://react.dev/reference/react/useEffectEvent) automaticamente. ```tsx // components/SearchTracker.tsx // Corretto: Effect Event utilizzato all'interno di useEffect import { useEffect, useEffectEvent, useState } from 'react' interface SearchTrackerProps { query: string userId: string } export function SearchTracker({ query, userId }: SearchTrackerProps) { const [results, setResults] = useState([]) // Tracciare le ricerche con il contesto utente corrente const onSearchComplete = useEffectEvent((resultCount: number) => { analytics.track('search_complete', { query, userId, // Sempre la userId aggiornata resultCount, timestamp: Date.now(), }) }) useEffect(() => { const controller = new AbortController() fetchSearchResults(query, controller.signal).then((data) => { setResults(data) onSearchComplete(data.length) // Chiamato dall'interno di useEffect }) return () => controller.abort() }, [query]) // userId esclusa in sicurezza — risiede nell'Effect Event return } ``` > **Non \u00e8 un sostituto del linter** > > useEffectEvent non \u00e8 una scappatoia per silenziare il linter delle dipendenze. Se un valore controlla realmente quando un effect deve rieseguirsi, appartiene al dependency array. La logica va estratta in un Effect Event solo quando rappresenta un'azione secondaria (logging, notifiche, analytics) che legge valori reattivi senza dover riattivare l'effect. ## Il componente Activity: pre-rendering in background con preservazione dello state Il componente `` (precedentemente denominato "Offscreen") controlla se i suoi figli sono visibili o nascosti. A differenza del rendering condizionale che distrugge lo state, o di CSS `display: none` che mantiene gli effect in esecuzione, `` preserva lo state mentre esegue il cleanup degli effect e posticipa gli aggiornamenti. ```tsx // components/TabLayout.tsx // Activity preserva lo state del form durante il cambio tab import { useState } from 'react' import { Activity } from 'react' interface Tab { id: string label: string content: React.ReactNode } export function TabLayout({ tabs }: { tabs: Tab[] }) { const [activeTab, setActiveTab] = useState(tabs[0].id) return (
{tabs.map((tab) => ( {tab.content} ))}
) } ``` Se un utente sta compilando un form nella scheda "Profilo", pu\u00f2 passare alla scheda "Impostazioni" e tornare indietro senza perdere i dati inseriti. Gli effect della scheda nascosta (timer, subscription, fetch di dati) vengono ripuliti, liberando risorse. Quando la scheda torna visibile, gli effect vengono rimontati e lo state viene ripristinato istantaneamente. ## Modalit\u00e0 Activity: visible e hidden nel dettaglio Il componente `` accetta una prop `mode` con due valori: | Comportamento | `mode="visible"` | `mode="hidden"` | |----------|-------------------|------------------| | Rendering DOM | Normale | `display: none` via CSS | | State del componente | Attivo | Preservato in memoria | | Effect (useEffect) | Montati | Ripuliti | | Priorit\u00e0 aggiornamenti | Normale | Posticipata a idle | | Pre-rendering | N/A | Rendering a bassa priorit\u00e0 | Quando un componente parte nascosto (primo render con `mode="hidden"`), React lo renderizza a bassa priorit\u00e0 senza montare gli effect. Questo abilita la navigazione istantanea: la pagina di destinazione \u00e8 gi\u00e0 renderizzata in background quando l'utente fa clic. ```tsx // components/PrerenderedRoute.tsx // Pre-renderizzare la prossima route probabile in background import { Activity, Suspense, use } from 'react' interface PrerenderedRouteProps { isActive: boolean dataPromise: Promise } export function PrerenderedRoute({ isActive, dataPromise }: PrerenderedRouteProps) { return ( }> ) } function DashboardContent({ dataPromise }: { dataPromise: Promise }) { // use() legge la promise in cache — funziona durante il pre-render nascosto const data = use(dataPromise) return (
) } ``` L'`` nascosta pre-renderizza la dashboard a priorit\u00e0 idle. Combinata con `Suspense` e l'API `use`, il caricamento dei dati avviene in background. Quando `isActive` passa a `true`, il contenuto appare senza alcun indicatore di caricamento. ## Activity e TanStack Query: l'insidia della cache Un errore frequente con `` riguarda [TanStack Query](https://tanstack.com/query/latest). Poich\u00e9 `useQuery` si basa internamente su `useEffect`, le query all'interno di un'`` nascosta non verranno eseguite: l'effect \u00e8 smontato. ```tsx // components/UserDashboard.tsx // Problema: useQuery non effettua il fetch quando Activity \u00e8 nascosta import { useQuery, useQueryClient } from '@tanstack/react-query' import { Activity } from 'react' function UserStats() { // Questa useQuery NON verr\u00e0 eseguita mentre \u00e8 nascosta const { data } = useQuery({ queryKey: ['user-stats'], queryFn: fetchUserStats, }) return } // Soluzione: precaricare i dati al di fuori del confine Activity function DashboardWithPrefetch({ showStats }: { showStats: boolean }) { const queryClient = useQueryClient() // Prefetch al livello genitore — eseguito indipendentemente dalla modalit\u00e0 Activity queryClient.ensureQueryData({ queryKey: ['user-stats'], queryFn: fetchUserStats, }) return ( ) } ``` La soluzione \u00e8 diretta: spostare il prefetch dei dati in un componente genitore o utilizzare `queryClient.ensureQueryData` al di fuori del confine ``. I dati in cache saranno disponibili quando il componente nascosto torner\u00e0 visibile. > **Compromesso sulla memoria** > > Activity scambia memoria per velocit\u00e0. Ogni albero di componenti nascosto rimane in memoria con il suo DOM completo. Per applicazioni con molte route nascoste, \u00e8 consigliabile monitorare il consumo di memoria. Il team React sta esplorando l'eliminazione automatica delle Activity nascoste meno utilizzate nelle versioni future. ## Combinare useEffectEvent e Activity Entrambe le API si completano a vicenda nei pattern di navigazione reali. Uno scenario comune: una dashboard con tab in cui ogni scheda ha subscription WebSocket e tracking analytics. ```tsx // components/LiveDashboard.tsx import { useEffect, useEffectEvent, useState } from 'react' import { Activity } from 'react' function LiveFeed({ channel, userId }: { channel: string; userId: string }) { const [messages, setMessages] = useState([]) // Tracking analytics con userId corrente — nessuna risincronizzazione dell'effect const onNewMessage = useEffectEvent((msg: Message) => { analytics.track('live_message', { channel, userId }) setMessages((prev) => [...prev, msg]) }) useEffect(() => { const ws = new WebSocket(`wss://api.example.com/${channel}`) ws.onmessage = (event) => { const msg = JSON.parse(event.data) as Message onNewMessage(msg) } return () => ws.close() }, [channel]) // Riconnessione pulita solo al cambio canale return } export function LiveDashboard({ userId }: { userId: string }) { const [activeChannel, setActiveChannel] = useState('general') const channels = ['general', 'alerts', 'metrics'] return (
{channels.map((ch) => ( ))}
) } ``` Durante il cambio canale, il WebSocket del feed nascosto viene disconnesso (cleanup dell'effect tramite ``). La cronologia dei messaggi sopravvive nello state. L'Effect Event `onNewMessage` garantisce che gli analytics facciano sempre riferimento alla `userId` corrente senza forzare riconnessioni WebSocket. ## Domande da colloquio: useEffectEvent e Activity Queste domande testano la comprensione delle nuove API e della loro interazione con il modello di rendering di React. Sono sempre pi\u00f9 comuni nei [colloqui React](/technologies/react-next/interview-questions/react-hooks) presso aziende che utilizzano React 19.2. **D1: Quale problema risolve useEffectEvent che useCallback non pu\u00f2 risolvere?** `useCallback` crea una funzione memoizzata, che deve comunque essere inserita nel dependency array dell'effect. Se una delle sue dipendenze cambia, il callback cambia, il che riesegue l'effect. `useEffectEvent` crea una funzione che legge sempre i valori pi\u00f9 recenti senza essere una dipendenza: l'effect non viene mai rieseguito per causa sua. Questa separazione \u00e8 impossibile con il solo `useCallback`. **D2: Un Effect Event pu\u00f2 essere passato come prop a un componente figlio?** No. Gli Effect Event sono progettati per essere chiamati esclusivamente dall'interno di `useEffect` o di altri Effect Event. La loro identit\u00e0 cambia a ogni render, quindi passarli come props causerebbe re-render non necessari e infrangerebbe il modello mentale. Il [plugin ESLint applica questa regola](https://react.dev/reference/react/useEffectEvent). **D3: In che modo Activity differisce dal rendering condizionale e da CSS display:none?** Il rendering condizionale (`{show && }`) smonta completamente il componente, distruggendo lo state. CSS `display: none` nasconde visivamente ma mantiene tutti gli effect in esecuzione, sprecando risorse. `` preserva lo state, esegue il cleanup degli effect, posticipa gli aggiornamenti a priorit\u00e0 idle e pu\u00f2 pre-renderizzare il contenuto in background. **D4: Cosa succede a useEffect all'interno di un Activity nascosto?** Quando un `` passa a `mode="hidden"`, React esegue tutte le funzioni di cleanup degli effect (il valore di ritorno di `useEffect`). Nessun nuovo effect viene montato finch\u00e9 il componente resta nascosto. Quando torna visibile, gli effect si rimontano con lo state preservato. Per questo le [librerie di data fetching](/technologies/react-next/interview-questions/react-query-tanstack) che si basano su `useEffect` necessitano di strategie di prefetching al di fuori del confine Activity. **D5: Come si pre-renderizza una route con Activity e Suspense?** La route va avvolta in `` con un boundary `` all'interno. Per il fetch dei dati si utilizza l'API `use()` o una sorgente dati compatibile con Suspense. React renderizza l'albero nascosto a bassa priorit\u00e0, risolvendo il boundary Suspense in background. Quando l'utente naviga e `mode` diventa `"visible"`, il contenuto completamente renderizzato appare istantaneamente senza stato di caricamento. **D6: useEffectEvent \u00e8 un sostituto della regola lint exhaustive-deps?** No. La [regola exhaustive-deps](https://react.dev/reference/react/useEffect) rimane fondamentale per individuare dipendenze genuinamente mancanti. `useEffectEvent` gestisce un caso specifico: logica che legge valori reattivi ma non deve controllare quando l'effect si riesegue (analytics, notifiche, logging). Utilizzarlo per sopprimere tutti gli avvisi sulle dipendenze nasconde bug e ne vanifica lo scopo. ## Conclusione - `useEffectEvent` sostituisce il workaround con `useRef` per le stale closure negli effect, con supporto nativo del linter in `eslint-plugin-react-hooks@6+` - Gli Effect Event leggono sempre le props e lo state pi\u00f9 recenti senza attivare la risincronizzazione dell'effect \u2014 ideali per analytics, logging e callback di notifica - `` preserva lo state dei componenti eseguendo il cleanup degli effect, offrendo un compromesso tra rendering condizionale e nascondimento CSS - Le Activity nascoste pre-renderizzano a priorit\u00e0 idle, abilitando la navigazione istantanea in combinazione con `Suspense` e l'API `use` - [TanStack Query](/technologies/react-next/interview-questions/react-query-tanstack) e altre librerie basate su effect necessitano di prefetching al di fuori dei confini Activity, poich\u00e9 `useEffect` non viene eseguito in modalit\u00e0 nascosta - Entrambe le API sono disponibili a partire da React 19.2 \u2014 aggiornare ESLint e React insieme per il pieno supporto degli strumenti --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/react-next/react-19-useeffectevent-activity-new-apis-interview-questions