# React 19 useEffectEvent und Activity: Neue APIs und Interviewfragen 2026 > Ausführlicher Leitfaden zu useEffectEvent und der Activity-Komponente in React 19.2 – mit Codebeispielen, Best Practices und häufigen Interviewfragen für 2026. - Published: 2026-06-03 - Updated: 2026-06-03 - Author: SharpSkill - Tags: react, react-19, useEffectEvent, activity, interview - Reading time: 9 min --- Mit React 19.2 hat das React-Team zwei APIs veröffentlicht, die langjährige Schwachstellen in der Entwicklung adressieren: `useEffectEvent` beseitigt veraltete Closures in Effects, und `` ermöglicht das Vorrendern im Hintergrund mit vollständiger State-Erhaltung. Beide APIs wurden im [Oktober 2025](https://react.dev/blog/2025/10/01/react-19-2) ausgeliefert und verändern bereits die Art und Weise, wie produktive React-Anwendungen Seiteneffekte und Navigation handhaben. > **React 19.2 Mindestversion** > > useEffectEvent und Activity erfordern React 19.2 oder höher. Das Update erfolgt mit `npm install react@latest react-dom@latest`. Das ESLint-Plugin `eslint-plugin-react-hooks@6+` bietet native Unterstützung für useEffectEvent in Dependency-Arrays. ## Das Problem veralteter Closures: Warum useEffectEvent nötig ist Jede Entwicklerin und jeder Entwickler, der mit React arbeitet, kennt das Problem veralteter Closures. Ein Effect erfasst einen Wert zum Zeitpunkt des Renderns, und wenn sich dieser Wert ändert, referenziert der Effect immer noch den alten. Die klassische Umgehung bestand darin, `useRef` zu verwenden, um eine mutable Referenz zu halten – funktional, aber umständlich und für den Linter unsichtbar. Ein typisches Beispiel ist eine Chat-Anwendung, die Analytics protokolliert, wenn eine neue Nachricht eintrifft. Das Log soll das aktuelle `theme` enthalten, aber Theme-Änderungen sollen die Chat-Verbindung nicht neu aufbauen: ```tsx // hooks/useChatRoom.ts // Vor useEffectEvent: useRef-Workaround import { useEffect, useRef } from 'react' export function useChatRoom(roomId: string, theme: string) { // Theme in einem Ref speichern, um veraltete Closures zu vermeiden const themeRef = useRef(theme) themeRef.current = theme useEffect(() => { const connection = createConnection(roomId) connection.on('message', (msg) => { // themeRef.current hat immer den aktuellen Wert logAnalytics('new_message', { roomId, theme: themeRef.current }) showNotification(msg) }) connection.connect() return () => connection.disconnect() }, [roomId]) // theme absichtlich ausgelassen — aber der Linter warnt } ``` Dieser Ansatz funktioniert, aber der Linter meldet `theme` als fehlende Abhängigkeit. Die Warnung zu unterdrücken verbirgt potenzielle Fehler an anderer Stelle. Das `useRef`-Muster verschleiert zudem die Absicht: Neue Teammitglieder müssen nachvollziehen, warum `theme` in einem Ref statt im Dependency-Array steht. ## useEffectEvent: Reaktive und nicht-reaktive Logik sauber trennen Der `useEffectEvent`-Hook erstellt eine stabile Funktion, die stets die neuesten Props und den aktuellen State liest, ohne eine erneute Synchronisierung des Effects auszulösen. Er ersetzt das `useRef`-Muster durch eine deklarative API, die der Linter nativ versteht. ```tsx // hooks/useChatRoom.ts // Nach useEffectEvent: saubere Trennung der Zuständigkeiten import { useEffect, useEffectEvent } from 'react' export function useChatRoom(roomId: string, theme: string) { // Effect Event: liest immer das aktuelle Theme, löst keinen Reconnect aus 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]) // Keine Linter-Warnung — onMessage ist ein Effect Event } ``` Der Effect wird nur erneut ausgeführt, wenn sich `roomId` ändert. Der `onMessage`-Callback sieht immer das aktuelle `theme`, ohne als Abhängigkeit aufgeführt zu werden. Die [offizielle React-Dokumentation](https://react.dev/reference/react/useEffectEvent) stellt ausdrücklich klar, dass Effect Events ausschließlich innerhalb von Effects aufgerufen werden dürfen – niemals beim Rendern oder als Props an Kindkomponenten. ## Regeln und Einschränkungen von useEffectEvent Drei Regeln bestimmen die Verwendung von `useEffectEvent`: 1. Aufruf auf der obersten Ebene einer Komponente oder eines Custom Hooks – niemals innerhalb von Schleifen oder Bedingungen 2. Die zurückgegebene Funktion darf nur innerhalb von `useEffect` oder einem anderen Effect Event aufgerufen werden 3. Sie darf nicht als Prop weitergegeben oder von einem Hook für externen Gebrauch zurückgegeben werden Ein Verstoß gegen Regel 2 verursacht subtile Fehler: Die Funktionsidentität ändert sich bei jedem Render, sodass das Speichern in einem Ref oder die Weitergabe nach unten den Zweck zunichtemacht. Das `eslint-plugin-react-hooks@6+` [erzwingt diese Einschränkungen](https://react.dev/reference/react/useEffectEvent) automatisch. ```tsx // components/SearchTracker.tsx // Korrekt: Effect Event innerhalb von useEffect verwendet import { useEffect, useEffectEvent, useState } from 'react' interface SearchTrackerProps { query: string userId: string } export function SearchTracker({ query, userId }: SearchTrackerProps) { const [results, setResults] = useState([]) // Suchanfragen mit aktuellem Benutzerkontext tracken const onSearchComplete = useEffectEvent((resultCount: number) => { analytics.track('search_complete', { query, userId, // Immer die aktuelle userId resultCount, timestamp: Date.now(), }) }) useEffect(() => { const controller = new AbortController() fetchSearchResults(query, controller.signal).then((data) => { setResults(data) onSearchComplete(data.length) // Innerhalb von useEffect aufgerufen }) return () => controller.abort() }, [query]) // userId sicher ausgelassen — lebt im Effect Event return } ``` > **Kein Ersatz für den Dependency-Linter** > > useEffectEvent ist kein Fluchtmechanismus, um den Dependency-Linter stumm zu schalten. Wenn ein Wert tatsächlich steuert, wann ein Effect erneut ausgeführt werden soll, gehört er ins Dependency-Array. Logik sollte nur dann in ein Effect Event ausgelagert werden, wenn sie eine Nebenaktion darstellt (Logging, Benachrichtigungen, Analytics), die reaktive Werte liest, ohne den Effect erneut auslösen zu müssen. ## Die Activity-Komponente: Hintergrund-Pre-Rendering mit State-Erhaltung Die ``-Komponente (ehemals "Offscreen") steuert, ob ihre Kinder sichtbar oder verborgen sind. Im Gegensatz zu konditionellem Rendering, das den State zerstört, oder CSS `display: none`, das Effects weiterlaufen lässt, erhält `` den State, räumt Effects auf und verschiebt Updates auf Leerlaufpriorität. ```tsx // components/TabLayout.tsx // Activity erhält Formular-State beim Tab-Wechsel 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} ))}
) } ``` Wenn Benutzer ein Formular im Tab "Profil" ausfüllen, können sie zum Tab "Einstellungen" wechseln und zurückkehren, ohne Eingaben zu verlieren. Die Effects des verborgenen Tabs (Timer, Subscriptions, Datenabfragen) werden aufgeräumt und Ressourcen freigegeben. Sobald der Tab wieder sichtbar wird, werden die Effects erneut eingehängt und der State sofort wiederhergestellt. ## Activity-Modi: Visible und Hidden im Detail Die ``-Komponente akzeptiert eine `mode`-Prop mit zwei Werten: | Verhalten | `mode="visible"` | `mode="hidden"` | |----------|-------------------|------------------| | DOM-Rendering | Normal | `display: none` per CSS | | Komponenten-State | Aktiv | Im Speicher erhalten | | Effects (useEffect) | Eingehängt | Aufgeräumt | | Update-Priorität | Normal | Auf Leerlauf verschoben | | Pre-Rendering | N/A | Rendering mit niedriger Priorität | Wenn eine Komponente initial verborgen startet (erstes Rendering mit `mode="hidden"`), rendert React sie mit niedriger Priorität, ohne Effects einzuhängen. Das ermöglicht sofortige Navigation: Die Zielseite ist bereits im Hintergrund gerendert, wenn der Benutzer klickt. ```tsx // components/PrerenderedRoute.tsx // Die nächste wahrscheinliche Route im Hintergrund vorrendern 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() liest das gecachte Promise — funktioniert beim Hidden-Pre-Render const data = use(dataPromise) return (
) } ``` Die verborgene `` rendert das Dashboard mit Leerlaufpriorität vor. In Kombination mit `Suspense` und der `use`-API erfolgt das Datenladen im Hintergrund. Wenn `isActive` auf `true` wechselt, erscheint der Inhalt sofort ohne Ladeindikator. ## Activity und TanStack Query: Der Cache-Fallstrick Ein häufiger Fehler bei `` betrifft [TanStack Query](https://tanstack.com/query/latest). Da `useQuery` intern auf `useEffect` basiert, werden Queries innerhalb einer verborgenen `` nicht ausgeführt – der Effect ist ausgehängt. ```tsx // components/UserDashboard.tsx // Problem: useQuery ruft keine Daten ab, wenn Activity verborgen ist import { useQuery, useQueryClient } from '@tanstack/react-query' import { Activity } from 'react' function UserStats() { // Diese useQuery wird NICHT laufen, solange sie verborgen ist const { data } = useQuery({ queryKey: ['user-stats'], queryFn: fetchUserStats, }) return } // Lösung: Daten außerhalb der Activity-Grenze vorladen function DashboardWithPrefetch({ showStats }: { showStats: boolean }) { const queryClient = useQueryClient() // Prefetch auf Elternebene — läuft unabhängig vom Activity-Modus queryClient.ensureQueryData({ queryKey: ['user-stats'], queryFn: fetchUserStats, }) return ( ) } ``` Die Lösung ist unkompliziert: Das Vorladen der Daten wird in eine übergeordnete Komponente verlagert, oder es wird `queryClient.ensureQueryData` außerhalb der ``-Grenze verwendet. Die gecachten Daten stehen dann zur Verfügung, sobald die verborgene Komponente sichtbar wird. > **Speicher-Kompromiss** > > Activity tauscht Speicher gegen Geschwindigkeit. Jeder verborgene Komponentenbaum bleibt mit seinem gesamten DOM im Speicher. Bei Anwendungen mit vielen verborgenen Routen sollte der Speicherverbrauch überwacht werden. Das React-Team arbeitet an einer automatischen Bereinigung der am längsten ungenutzten verborgenen Activities in zukünftigen Versionen. ## useEffectEvent und Activity kombinieren Beide APIs ergänzen sich in realen Navigationsmustern. Ein häufiges Szenario: Ein Dashboard mit Tabs, in dem jeder Tab WebSocket-Subscriptions und Analytics-Tracking aufweist. ```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([]) // Analytics-Tracking mit aktueller userId — kein Effect-Re-Sync 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]) // Saubere Neuverbindung nur bei Kanalwechsel return } export function LiveDashboard({ userId }: { userId: string }) { const [activeChannel, setActiveChannel] = useState('general') const channels = ['general', 'alerts', 'metrics'] return (
{channels.map((ch) => ( ))}
) } ``` Beim Kanalwechsel wird die WebSocket-Verbindung des verborgenen Feeds getrennt (Effect-Cleanup durch ``). Der Nachrichtenverlauf bleibt im State erhalten. Das `onNewMessage` Effect Event stellt sicher, dass Analytics immer die aktuelle `userId` referenzieren, ohne WebSocket-Neuverbindungen zu erzwingen. ## Interviewfragen: useEffectEvent und Activity Diese Fragen testen das Verständnis der neuen APIs und ihrer Interaktion mit dem Rendering-Modell von React. Sie tauchen zunehmend in [React-Interviews](/technologies/react-next/interview-questions/react-hooks) bei Unternehmen auf, die React 19.2 einsetzen. **F1: Welches Problem löst useEffectEvent, das useCallback nicht lösen kann?** `useCallback` erstellt eine memoierte Funktion, die aber trotzdem im Dependency-Array des Effects aufgeführt werden muss. Wenn sich eine ihrer eigenen Abhängigkeiten ändert, ändert sich der Callback, was den Effect erneut auslöst. `useEffectEvent` erstellt eine Funktion, die stets die neuesten Werte liest, ohne eine Abhängigkeit zu sein – der Effect wird dadurch niemals erneut ausgeführt. Diese Trennung ist mit `useCallback` allein unmöglich. **F2: Kann ein Effect Event als Prop an eine Kindkomponente weitergegeben werden?** Nein. Effect Events sind dafür konzipiert, ausschließlich innerhalb von `useEffect` oder anderen Effect Events aufgerufen zu werden. Ihre Identität ändert sich bei jedem Render, sodass die Weitergabe als Props unnötige Re-Renders verursachen und das mentale Modell brechen würde. Das [ESLint-Plugin erzwingt diese Regel](https://react.dev/reference/react/useEffectEvent). **F3: Wie unterscheidet sich Activity von konditionellem Rendering und CSS display:none?** Konditionelles Rendering (`{show && }`) unmountet die Komponente vollständig – der State wird zerstört. CSS `display: none` versteckt visuell, lässt aber alle Effects weiterlaufen und verschwendet Ressourcen. `` erhält den State, räumt Effects auf, verschiebt Updates auf Leerlaufpriorität und kann Inhalte im Hintergrund vorrendern. **F4: Was passiert mit useEffect innerhalb einer verborgenen Activity?** Wenn eine `` zu `mode="hidden"` wechselt, führt React alle Cleanup-Funktionen der Effects aus (der Rückgabewert von `useEffect`). Solange die Komponente verborgen ist, werden keine neuen Effects eingehängt. Wird sie wieder sichtbar, werden die Effects mit dem erhaltenen State erneut eingehängt. Deshalb benötigen [Datenlade-Bibliotheken](/technologies/react-next/interview-questions/react-query-tanstack), die auf `useEffect` basieren, Prefetching-Strategien außerhalb der Activity-Grenze. **F5: Wie lässt sich eine Route mit Activity und Suspense vorrendern?** Die Route wird in `` mit einer inneren ``-Boundary eingebettet. Für das Laden der Daten werden die `use()`-API oder eine Suspense-fähige Datenquelle verwendet. React rendert den verborgenen Baum mit niedriger Priorität und löst die Suspense-Boundary im Hintergrund auf. Wenn der Benutzer navigiert und `mode` auf `"visible"` wechselt, erscheint der vollständig gerenderte Inhalt sofort ohne Ladezustand. **F6: Ist useEffectEvent ein Ersatz für die exhaustive-deps-Lint-Regel?** Nein. Die [exhaustive-deps-Regel](https://react.dev/reference/react/useEffect) bleibt entscheidend für das Erkennen genuiner fehlender Abhängigkeiten. `useEffectEvent` behandelt einen spezifischen Fall: Logik, die reaktive Werte liest, aber nicht steuern soll, wann der Effect erneut läuft (Analytics, Benachrichtigungen, Logging). Es zur Unterdrückung aller Dependency-Warnungen zu verwenden, verbirgt Fehler und verfehlt den Zweck. ## Fazit - `useEffectEvent` ersetzt den `useRef`-Workaround für veraltete Closures in Effects, mit nativer Linter-Unterstützung in `eslint-plugin-react-hooks@6+` - Effect Events lesen stets die neuesten Props und den aktuellen State, ohne eine erneute Effect-Synchronisierung auszulösen – ideal für Analytics, Logging und Benachrichtigungs-Callbacks - `` erhält den Komponenten-State, während Effects aufgeräumt werden, und bietet einen Mittelweg zwischen konditionellem Rendering und CSS-Verstecken - Verborgene Activities rendern mit Leerlaufpriorität vor und ermöglichen sofortige Navigation in Kombination mit `Suspense` und der `use`-API - [TanStack Query](/technologies/react-next/interview-questions/react-query-tanstack) und andere Effect-basierte Bibliotheken benötigen Prefetching außerhalb der Activity-Grenzen, da `useEffect` im verborgenen Modus nicht ausgeführt wird - Beide APIs sind ab React 19.2 verfügbar – ESLint und React sollten gemeinsam aktualisiert werden, um die volle Tooling-Unterstützung zu erhalten --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/react-next/react-19-useeffectevent-activity-new-apis-interview-questions