Zum Hauptinhalt springen

React mit Cursor debuggen: Hooks, State und Performance

Cursor React Debugging

React-Anwendungen können aufgrund von State-Management, Hook-Abhängigkeiten und Rendering-Optimierung komplexe Bugs aufweisen. Die KI-Funktionen von Cursor können Ihnen helfen, den State-Flow zu verfolgen, unnötige Re-Renders zu identifizieren und Hook-Probleme zu debuggen. Dieser Leitfaden deckt gängige React-Debugging-Szenarien ab.

Voraussetzungen​

Bevor Sie beginnen:

  • Cursor installiert (neueste Version empfohlen)
  • React DevTools Browser-Erweiterung
  • Vertrautheit mit React Hooks (useState, useEffect, useMemo)
  • Verständnis des React-Renderzyklus

Debug-Umgebung einrichten​

Empfohlene Cursor-Einstellungen​

// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"typescript.preferences.importModuleSpecifier": "relative",
"editor.quickSuggestions": {
"strings": true
}
}

React DevTools-Integration​

  1. Installieren Sie React DevTools für Ihren Browser
  2. Öffnen Sie Ihr Projekt in Cursor
  3. Verwenden Sie den Components-Tab zur Inspektion der Komponentenbaum
  4. Verwenden Sie den Profiler-Tab zur Messung der Render-Performance

React Hooks debuggen​

useEffect-Abhängigkeitsprobleme​

Der häufigste Fehler mit Hooks betrifft fehlende Abhängigkeiten:

// ❌ Fehler: Fehlende Abhängigkeit
useEffect(() => {
fetchUser(userId);
}, []); // eslint-disable-line react-hooks/exhaustive-deps

Lassen Sie Cursor korrigieren:

Dieser useEffect hat eine fehlende Abhängigkeitswarnung. Behebe sie und vermeide dabei Endlosschleifen.

Cursor schlägt vor:

// ✅ Richtig: Alle Abhängigkeiten einbeziehen
useEffect(() => {
fetchUser(userId);
}, [userId]); // userId ist jetzt enthalten

Benutzerdefinierte Hooks debuggen​

// src/hooks/useLocalStorage.ts
import { useState, useEffect, useCallback } from 'react';

export function useLocalStorage<T>(key: string, initialValue: T) {
// Faule Initialisierung, um localStorage bei jedem Render zu vermeiden
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});

const setValue = useCallback(
(value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
},
[key, storedValue]
);

return [storedValue, setValue] as const;
}

State-Flow debuggen​

Prop Drilling verfolgen​

Wenn Daten durch mehrere Ebenen gehen, verwenden Sie Cursor zum Refactoring auf Context:

// ❌ Vorher: Prop Drilling
function App() {
const [user, setUser] = useState(null);
return <Layout user={user} setUser={setUser} />;
}

function Layout({ user, setUser }) {
return <Header user={user} setUser={setUser} />;
}

// ✅ Nachher: Context verwenden
const UserContext = createContext(null);

function App() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
<Layout />
</UserContext.Provider>
);
}

Context-Consumer debuggen​

// Verwendung von useContextSelector für granuläre Abonnements
import { createContext, useContext, useCallback } from 'react';

const ThemeContext = createContext(null);

export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}

// In der Komponente
function ThemedButton() {
const { theme, toggleTheme } = useTheme();

// Re-render nur wenn theme sich ändert, nicht alles im Context
return (
<button
style={{ background: theme.primary }}
onClick={toggleTheme}
>
Toggle Theme
</button>
);
}

Performance debuggen​

Unnötige Renders identifizieren​

Verwenden Sie den React DevTools Profiler:

  1. Öffnen Sie den Profiler-Tab
  2. Klicken Sie auf Record (Aufzeichnen)
  3. Interagieren Sie mit der Anwendung
  4. Stoppen Sie die Aufzeichnung und analysieren Sie das Flammendiagramm

Optimierung mit memo​

import { memo, useMemo, useCallback } from 'react';

// Komponente wrappen, um unnötige Renders zu verhindern
const ExpensiveList = memo(function ExpensiveList({ items, onSelect }) {
return (
<ul>
{items.map(item => (
<ListItem key={item.id} item={item} onSelect={onSelect} />
))}
</ul>
);
});

// In der Elternkomponente
function Parent() {
const [items, setItems] = useState([]);
const [filter, setFilter] = useState('');

// Gefilterte Liste memorieren
const filteredItems = useMemo(
() => items.filter(item => item.name.includes(filter)),
[items, filter]
);

// Callback memorieren, um Referenzgleichheit zu wahren
const handleSelect = useCallback((id: string) => {
console.log('Selected:', id);
}, []);

return (
<ExpensiveList items={filteredItems} onSelect={handleSelect} />
);
}

useMemo und useCallback Guide​

HookVerwendet fürBeispiel
useMemoTeure BerechnungenFiltern/Sortieren großer Arrays
useCallbackCallbacks an Kinder übergebenEvent-Handler
memoReine KomponentenoptimierungGroße Listen rendern
Vorzeitige Optimierung

Profilen Sie die Performance, bevor Sie useMemo/useCallback hinzufügen. Cursor kann Ihnen helfen, echte Performance-Engpässe zu identifizieren.

Häufige React-Fehler​

Endlosschleifen​

Too many re-renders. React limits the number of renders to prevent an infinite loop.

Ursache : State direkt während des Renderns setzen

// ❌ Fehler: State während des Renderns setzen
function BadComponent() {
const [count, setCount] = useState(0);
setCount(count + 1); // Verursacht Endlosschleife
return <div>{count}</div>;
}

// ✅ Richtig: State im Event-Handler setzen
function GoodComponent() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
}

Veralteter Closure​

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
const interval = setInterval(() => {
// ❌ Fehler: 'count' ist immer 0
console.log(count);
setCount(count + 1);
}, 1000);

return () => clearInterval(interval);
}, []); // Fehlende count-Abhängigkeit

// ✅ Richtig: Funktionales Update verwenden
useEffect(() => {
const interval = setInterval(() => {
setCount(c => c + 1); // Verwendet immer den neuesten Wert
}, 1000);

return () => clearInterval(interval);
}, []);

return <div>{count}</div>;
}

Hydration-Mismatch​

Warning: Text content did not match. Server: "..." Client: "..."

Verwenden Sie Cursor, um SSR-Probleme zu beheben:

// Dynamischer Inhalt, der nur clientseitig gerendert wird
function ClientDate() {
const [date, setDate] = useState<string | null>(null);

useEffect(() => {
setDate(new Date().toLocaleDateString());
}, []);

if (!date) return <span>Loading...</span>;
return <span>{date}</span>;
}

Asynchrone Operationen debuggen​

Daten-Fetching-Pattern​

// src/hooks/useApi.ts
import { useState, useEffect, useRef } from 'react';

export function useApi<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const abortController = useRef<AbortController | null>(null);

useEffect(() => {
abortController.current = new AbortController();

async function fetchData() {
try {
setLoading(true);
setError(null);

const response = await fetch(url, {
signal: abortController.current?.signal,
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const result = await response.json();
setData(result);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err);
}
} finally {
setLoading(false);
}
}

fetchData();

return () => {
abortController.current?.abort();
};
}, [url]);

return { data, loading, error };
}

Error Boundaries mit Cursor​

// src/components/ErrorBoundary.tsx
import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
children: ReactNode;
fallback?: ReactNode;
}

interface State {
hasError: boolean;
error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };

static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught:', error, errorInfo);
// Fehler an Überwachungsdienst senden
}

render() {
if (this.state.hasError) {
return (
this.props.fallback || (
<div role="alert">
<h2>Something went wrong</h2>
<details>
<summary>Error details</summary>
<pre>{this.state.error?.message}</pre>
</details>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
)
);
}

return this.props.children;
}
}

Schnelle Debug-Checkliste​

ProblemÜberprüfen
Komponente rendert zu oftReact DevTools Profiler
State aktualisiert sich nichtuseEffect-Abhängigkeiten
Context-Wert veraltetProvider-Wert-Referenz
Memory LeakuseEffect-Cleanup-Funktion
Hydration-FehlerClient/Server-Inhaltsunterschied
EndlosschleifeState-Update während des Renderns

Verwandte Ressourcen​

Zuletzt aktualisiert: Juli 2025