v1.6: Login-Race behoben (schwarzer Bildschirm nach Anmeldung)

- Store setzt 'me' erst nach dem Laden der Bootstrap-Daten (Start, Login,
  Registrierung) - vorher rendereten Seiten gegen leere Daten und brachen ab;
  Seite lief erst nach manuellem Reload (Race zwischen setMe und ladeAlles)
- FehlerGrenze (ErrorBoundary) in App.tsx: Render-Fehler zeigen Hinweis +
  Reload-Button statt schwarzem Bildschirm
This commit is contained in:
2026-10-02 13:56:03 +02:00
parent f1ceb44b47
commit 07d58ecb58
2 changed files with 38 additions and 6 deletions
+35 -3
View File
@@ -1,4 +1,5 @@
import { Routes, Route, Navigate } from 'react-router' import { Routes, Route, Navigate } from 'react-router'
import { Component, type ReactNode } from 'react'
import { StoreProvider, useStore } from './store/store' import { StoreProvider, useStore } from './store/store'
import AppLayout from './components/AppLayout' import AppLayout from './components/AppLayout'
import Login from './pages/Login' import Login from './pages/Login'
@@ -9,6 +10,35 @@ import Documents from './pages/Documents'
import Messages from './pages/Messages' import Messages from './pages/Messages'
import BenutzerVerwaltung from './pages/BenutzerVerwaltung' import BenutzerVerwaltung from './pages/BenutzerVerwaltung'
/** Fängt Render-Fehler ab – statt schwarzem Bildschirm: Hinweis + Reload. */
class FehlerGrenze extends Component<{ children: ReactNode }, { fehler: boolean }> {
state = { fehler: false }
static getDerivedStateFromError() {
return { fehler: true }
}
render() {
if (this.state.fehler) {
return (
<div className="flex min-h-screen items-center justify-center bg-[#22333b] p-6">
<div className="max-w-md rounded-2xl bg-white p-8 text-center shadow-2xl dark:bg-slate-900">
<h1 className="text-xl font-bold">Da ist etwas schiefgelaufen</h1>
<p className="mt-2 text-sm text-slate-500">
Die Seite konnte nicht korrekt dargestellt werden. Ein Neuladen hilft in der Regel weiter.
</p>
<button
onClick={() => window.location.reload()}
className="mt-5 rounded-xl bg-amber-500 px-6 py-2.5 font-semibold text-slate-900 hover:bg-amber-400"
>
Seite neu laden
</button>
</div>
</div>
)
}
return this.props.children
}
}
function Ladebildschirm() { function Ladebildschirm() {
return ( return (
<div className="flex min-h-screen items-center justify-center bg-[#22333b]"> <div className="flex min-h-screen items-center justify-center bg-[#22333b]">
@@ -36,8 +66,9 @@ function LoginGate() {
export default function App() { export default function App() {
return ( return (
<StoreProvider> <FehlerGrenze>
<Routes> <StoreProvider>
<Routes>
<Route path="/login" element={<LoginGate />} /> <Route path="/login" element={<LoginGate />} />
<Route <Route
element={ element={
@@ -55,6 +86,7 @@ export default function App() {
</Route> </Route>
<Route path="*" element={<Navigate to="/" replace />} /> <Route path="*" element={<Navigate to="/" replace />} />
</Routes> </Routes>
</StoreProvider> </StoreProvider>
</FehlerGrenze>
) )
} }
+3 -3
View File
@@ -112,9 +112,9 @@ export function StoreProvider({ children }: { children: ReactNode }) {
;(async () => { ;(async () => {
try { try {
const { benutzer } = await apiGet<{ benutzer: Benutzer }>('/api/auth/me') const { benutzer } = await apiGet<{ benutzer: Benutzer }>('/api/auth/me')
await ladeAlles() // erst Daten, dann me – sonst rendern Seiten gegen leere Daten
if (!aktiv) return if (!aktiv) return
setMe(benutzer) setMe(benutzer)
await ladeAlles()
} catch { } catch {
/* nicht angemeldet – Login-Seite zeigen */ /* nicht angemeldet – Login-Seite zeigen */
} finally { } finally {
@@ -148,8 +148,8 @@ export function StoreProvider({ children }: { children: ReactNode }) {
benutzername, benutzername,
passwortHash: await sha256(passwort), passwortHash: await sha256(passwort),
}) })
await ladeAlles() // erst Daten, dann me (s. useEffect)
setMe(benutzer) setMe(benutzer)
await ladeAlles()
return null return null
} catch (e) { } catch (e) {
return fehlerNachricht(e) return fehlerNachricht(e)
@@ -173,8 +173,8 @@ export function StoreProvider({ children }: { children: ReactNode }) {
email, email,
passwortHash: await sha256(passwort), passwortHash: await sha256(passwort),
}) })
await ladeAlles() // erst Daten, dann me (s. useEffect)
setMe(benutzer) setMe(benutzer)
await ladeAlles()
return null return null
} catch (e) { } catch (e) {
return fehlerNachricht(e) return fehlerNachricht(e)