companion-app: Fehlergrenze um die App - Absturz sichtbar statt schwarzer Seite

Ohne React-Error-Boundary unmountet ein unbehandelter Renderfehler die
gesamte App; übrig bleibt nur der Seitenhintergrund (im Nacht-Theme
praktisch schwarz) - passt zum gemeldeten "schwarze Seite nach Update
prüfen". Behebt nicht die eigentliche Ursache, macht einen künftigen
Absturz aber sichtbar (Fehlertext, Neu-laden-Knopf) und loggt ihn in
die Konsole statt ihn stumm verschwinden zu lassen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:04:06 +02:00
parent 0a0beb9eb3
commit f97ceab3ad
2 changed files with 55 additions and 1 deletions
+51
View File
@@ -0,0 +1,51 @@
/**
* Auffangnetz für Abstürze irgendwo im Baum.
*
* Ohne diese Grenze unmountet React die gesamte App bei jedem unbehandelten
* Fehler beim Rendern - #wurzel bleibt leer, und übrig bleibt nur der
* Seitenhintergrund (im Nacht-Theme praktisch schwarz). Genau das wurde als
* "schwarze Seite nach Update prüfen" gemeldet: kein hängender Ladezustand,
* sondern ein stiller Absturz, dessen eigentliche Fehlermeldung nirgends
* ankam. Ein "zurück und wieder rein" half nur, weil das erneute Öffnen den
* Baum komplett neu aufbaut und der auslösende Zustand dabei verschwindet.
*
* Diese Grenze behebt nicht die eigentliche Ursache - sie sorgt nur dafür,
* dass ein Absturz ab jetzt sichtbar ist (Fehlertext, Neu-laden-Knopf) statt
* unsichtbar zu bleiben, und dass der Fehlertext in der Konsole landet, um
* die eigentliche Ursache beim nächsten Mal fassen zu können.
*/
import { Component, type ReactNode } from "react"
import { ActionButton } from "@audi-dash/ui"
export class ErrorGrenze extends Component<
{ children: ReactNode },
{ fehler: Error | null }
> {
state: { fehler: Error | null } = { fehler: null }
static getDerivedStateFromError(fehler: Error) {
return { fehler }
}
componentDidCatch(fehler: Error, info: { componentStack?: string | null }) {
console.error("ErrorGrenze: unbehandelter Fehler beim Rendern", fehler, info.componentStack)
}
render() {
if (!this.state.fehler) return this.props.children
return (
<div className="dm-leer" style={{ minHeight: "100vh", justifyContent: "center" }}>
<span className="dm-leer__titel">Etwas ist schiefgelaufen</span>
<p className="dm-leer__text">
Die Seite konnte nicht dargestellt werden. Ein Neuladen behebt das in der Regel.
</p>
<p className="dm-leer__text" style={{ fontFamily: "monospace", fontSize: "12px" }}>
{this.state.fehler.message}
</p>
<ActionButton onClick={() => window.location.reload()}>Neu laden</ActionButton>
</div>
)
}
}
+4 -1
View File
@@ -6,6 +6,7 @@ import "./stile/grundlage.css"
import "./stile/audi-schrift.css"
import { nativeAblageEinrichten } from "./api"
import { App } from "./App"
import { ErrorGrenze } from "./ErrorGrenze"
// In der nativen Hülle den Token in den Schlüsselbund legen statt in
// localStorage. Ohne Hülle passiert hier nichts.
@@ -16,6 +17,8 @@ if (!wurzel) throw new Error("Wurzelelement #wurzel fehlt in index.html")
createRoot(wurzel).render(
<StrictMode>
<App />
<ErrorGrenze>
<App />
</ErrorGrenze>
</StrictMode>,
)