Fix blank stations page - add auth gate guard in App.tsx

ROOT CAUSE: Race condition where StationsPage renders before auth
token is ready, causing DOM state mismatch.

Timeline of the bug:
1. Auth0 sets isAuthenticated=true
2. App renders StationsPage before isAuthInitialized=true
3. useSavedStations hook is disabled (enabled: false)
4. Google Maps loads and manipulates DOM
5. Auth token finally acquired, isAuthInitialized=true
6. Component re-renders with query now enabled
7. React tries to remove DOM nodes already removed by Google Maps
8. NotFoundError: removeChild fails

SOLUTION: Add isAuthGateReady check in App.tsx before rendering
protected routes. Show "Initializing session..." until auth gate is
fully initialized.

Changes:
- Import useIsAuthInitialized hook in App.tsx
- Call hook in App component
- Add guard check after isAuthenticated check
- Show loading UI if authenticated but auth gate not ready
- Add debug logs to track render flow

Now the page won't render until BOTH:
1. isAuthenticated=true (Auth0)
2. isAuthInitialized=true (our token gate)

This prevents the race condition that causes the removeChild DOM error.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2025-11-04 19:26:21 -06:00
parent 050f1b030e
commit f1dd48808b

View File

@@ -6,6 +6,7 @@ import React, { useState, useEffect, useTransition, useCallback, lazy } from 're
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { Routes, Route, Navigate } from 'react-router-dom'; import { Routes, Route, Navigate } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react'; import { useAuth0 } from '@auth0/auth0-react';
import { useIsAuthInitialized } from './core/auth/auth-gate';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { ThemeProvider } from '@mui/material/styles'; import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from '@mui/material/CssBaseline';
@@ -237,7 +238,9 @@ const AddVehicleScreen: React.FC<AddVehicleScreenProps> = ({ onBack, onAdded })
function App() { function App() {
const { isLoading, isAuthenticated, user } = useAuth0(); const { isLoading, isAuthenticated, user } = useAuth0();
const isAuthGateReady = useIsAuthInitialized();
const [_isPending, startTransition] = useTransition(); const [_isPending, startTransition] = useTransition();
console.log('[DEBUG App] Render check - isLoading:', isLoading, 'isAuthenticated:', isAuthenticated, 'isAuthGateReady:', isAuthGateReady);
// Initialize data synchronization // Initialize data synchronization
const { prefetchForNavigation } = useDataSync(); const { prefetchForNavigation } = useDataSync();
@@ -380,6 +383,32 @@ function App() {
); );
} }
// Wait for auth gate to be ready before rendering protected routes
// This prevents a race condition where the page renders before the auth token is ready
if (!isAuthGateReady) {
console.log('[DEBUG App] Auth gate not ready yet, showing loading state');
if (mobileMode) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<Layout mobileMode={true}>
<div className="flex items-center justify-center h-64">
<div className="text-slate-500">Initializing session...</div>
</div>
</Layout>
</ThemeProvider>
);
}
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Initializing session...</div>
</div>
</ThemeProvider>
);
}
// Mobile app rendering // Mobile app rendering
if (mobileMode) { if (mobileMode) {
return ( return (