Häufige JSX-Fehler und wie man sie vermeidet
JSX sieht täuschend einfach aus – es ist doch nur HTML in JavaScript, oder? Dennoch stolpern selbst erfahrene Entwickler über seine Eigenheiten, besonders während sich React weiterentwickelt. Mit Reacts 19 automatischer JSX-Runtime, Server Components und der sich wandelnden Landschaft moderner Frameworks haben diese Fehler neue Auswirkungen. Hier erfahren Sie, worüber Entwickler immer noch stolpern und wie Sie diese Fallstricke vermeiden können.
Wichtigste Erkenntnisse
- Array-Indizes als Keys verursachen Reconciliation-Probleme und beeinträchtigen Reacts Concurrent-Features
- Server Components erfordern andere Muster als Client Components, insbesondere im Umgang mit Browser-APIs
- Die automatische JSX-Runtime verändert die Art der Code-Transformation und erfordert eine korrekte Konfiguration
- Inline-Funktionen und bedingte Rendering-Muster können die Performance stillschweigend beeinträchtigen
Die Evolution von JSX: Warum alte Gewohnheiten neuen Code brechen
Die automatische JSX-Runtime, die in React 17 eingeführt wurde, machte den Import von React in jeder Datei überflüssig, schuf aber auch neue Verwirrung. Ihr JSX wird nun anders transformiert – jsx-Funktionen ersetzen React.createElement, und falsch konfigurierte Build-Tools können Ihre App stillschweigend zum Absturz bringen.
Bei Server Components sind die Einsätze höher. JSX, das clientseitig perfekt funktioniert, stürzt ab, wenn es versucht, auf window zuzugreifen oder Hooks im falschen Kontext verwendet. Die Regeln haben sich nicht nur geändert; sie haben sich vervielfacht.
Kritische JSX-Fallstricke in modernem React
1. Instabile Keys, die die Performance zerstören
// ❌ Index als Key - verursacht Reconciliation-Probleme
items.map((item, index) => <Item key={index} {...item} />)
// ✅ Stabiler, eindeutiger Identifier
items.map(item => <Item key={item.id} {...item} />)
Die Verwendung von Array-Indizes als Keys bleibt einer der schädlichsten JSX-Fehler. In Reacts Concurrent-Features verursachen instabile Keys nicht nur Flackern – sie können Suspense-Boundaries brechen und unnötige Re-Renders im gesamten Component-Tree auslösen.
2. Direktes Rendern von Objekten
// ❌ Objekte sind keine gültigen React-Children
const user = { name: 'Alice', age: 30 };
return <div>{user}</div>;
// ✅ Spezifische Properties rendern
return <div>{user.name}</div>;
Diese Fehlermeldung hat sich seit React 15 nicht geändert, dennoch versuchen Entwickler immer noch, Objekte direkt zu rendern. Mit TypeScripts JSX-Inferenz fangen Sie dies zur Compile-Zeit ab – sofern Ihre tsconfig.json korrekt konfiguriert ist.
3. Inline-Funktionen erzeugen neue Referenzen
// ❌ Neue Funktion bei jedem Render
<Button onClick={() => handleClick(id)} />
// ✅ Stabile Referenz mit useCallback
const handleButtonClick = useCallback(() => handleClick(id), [id]);
<Button onClick={handleButtonClick} />
In Reacts Rendering-Pipeline verursachen Inline-Funktionen nicht nur Performance-Probleme – sie brechen die Memo-Optimierung und können kaskadierende Updates im gesamten Component-Tree auslösen.
Discover how at OpenReplay.com.
Server Components: Wo sich die JSX-Regeln ändern
4. Client-Only-Code in Server Components
// ❌ Stürzt in Server Components ab
export default function ServerComponent() {
const width = window.innerWidth; // ReferenceError
return <div style={{ width }} />;
}
// ✅ Use-Client-Direktive verwenden oder vom Client übergeben
'use client';
import { useState, useEffect } from 'react';
export default function ClientComponent() {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <div style={{ width }} />;
}
Server Components werden außerhalb des Browsers ausgeführt, wo DOM-APIs nicht existieren. Dies ist kein Konfigurationsproblem – es ist architektonisch bedingt.
5. Async Components ohne Suspense
// ❌ Unbehandeltes Promise in Server Component
async function UserProfile({ id }) {
const user = await fetchUser(id);
return <div>{user.name}</div>;
}
// ✅ Mit Suspense-Boundary umschließen
<Suspense fallback={<Loading />}>
<UserProfile id={userId} />
</Suspense>
React Server Components können asynchron sein, aber ohne entsprechende Suspense-Boundaries blockieren sie entweder das Rendering oder stürzen mit kryptischen Fehlermeldungen ab.
Moderne JSX-Konfigurationsfallen
6. Nicht übereinstimmende JSX-Runtime-Konfiguration
// ❌ Alte Transform in tsconfig.json
{
"compilerOptions": {
"jsx": "react" // Erfordert React-Imports
}
}
// ✅ Automatische Runtime für React 17+
{
"compilerOptions": {
"jsx": "react-jsx" // Kein React-Import erforderlich
}
}
Die automatische JSX-Runtime ist nicht nur eine Bequemlichkeit – sie ist für optimale Bundle-Größe und Server-Component-Kompatibilität erforderlich. Eine Fehlkonfiguration hier verursacht stille Fehler, die erst in der Produktion auftauchen.
7. Anti-Patterns beim bedingten Rendering
// ❌ Gibt 0 statt nichts zurück
{count && <Counter value={count} />}
// ✅ Explizite Boolean-Konvertierung
{Boolean(count) && <Counter value={count} />}
Wenn count gleich 0 ist, rendert JSX die Zahl 0, nicht nichts. Dieser Fehler ist besonders in React Native sichtbar, wo Text-Nodes entsprechende Container benötigen.
Präventionsstrategien
Konfigurieren Sie Ihre Tools richtig: Richten Sie ESLint mit eslint-plugin-react ein und aktivieren Sie diese Regeln:
react/jsx-keyreact/jsx-no-bindreact/display-name
Verwenden Sie TypeScript: Mit korrekter JSX-Konfiguration fängt TypeScript die meisten dieser Fehler zur Compile-Zeit ab. Aktivieren Sie den strict-Modus und konfigurieren Sie jsx korrekt in Ihrer tsconfig.json.
Verstehen Sie Ihre Runtime: Wissen Sie, ob Ihre Komponente auf dem Server oder Client läuft. Next.js 14+ macht dies mit der 'use client'-Direktive explizit, aber das mentale Modell gilt überall.
Fazit
JSX-Fehler im Jahr 2024 sind nicht nur eine Frage der Syntax – sie betreffen das Verständnis, wo und wie Ihr Code ausgeführt wird. Die automatische JSX-Runtime hat das Transformationsmodell verändert. Server Components haben das Ausführungsmodell verändert. Reacts Concurrent-Features haben das Performance-Modell verändert.
Beherrschen Sie diese Grundlagen, und Sie werden JSX schreiben, das nicht nur korrekt ist, sondern auch für die Möglichkeiten von modernem React optimiert ist. Das beste JSX ist unsichtbar – es tritt in den Hintergrund und lässt Ihre Komponenten glänzen.
Häufig gestellte Fragen
Wenn Sie Array-Indizes als Keys verwenden, kann React nicht richtig nachverfolgen, welche Items sich geändert, verschoben oder entfernt wurden. Dies zwingt React dazu, mehr Komponenten als notwendig neu zu rendern und kann dazu führen, dass der State nach einer Neuordnung der falschen Komponente zugeordnet wird.
Während Inline-Funktionen funktional arbeiten, erzeugen sie bei jedem Render neue Referenzen, was React.memo-Optimierungen bricht und möglicherweise dazu führt, dass Child-Komponenten unnötig neu gerendert werden. Für bessere Wartbarkeit verwenden Sie useCallback für Event-Handler, die von Props oder State abhängen.
Die react-Einstellung verwendet die klassische React.createElement-Transform, die React-Imports in jeder Datei erfordert. Die react-jsx-Einstellung verwendet die automatische Runtime, die in React 17 eingeführt wurde, welche die Transformation ohne explizite React-Imports handhabt und kleinere Bundles erzeugt.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.