Fix: History replaceState overuse

The commit addresses a "SecurityError: Attempt to use history.replaceState() more than 100 times per 10 seconds" error, likely caused by excessive calls to `history.replaceState()`.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-21 18:30:26 +00:00
parent 36a045d899
commit 63fd48a4a9
3 changed files with 70 additions and 33 deletions

View File

@ -1,6 +1,5 @@
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { Suspense, lazy } from 'react'; import { Suspense, lazy, useEffect } from 'react';
import { Toaster } from "@/components/ui/sonner"; import { Toaster } from "@/components/ui/sonner";
import Auth from './pages/Auth'; import Auth from './pages/Auth';
import ProtectedRoute from './components/auth/ProtectedRoute'; import ProtectedRoute from './components/auth/ProtectedRoute';
@ -19,22 +18,55 @@ const CartoesCredito = lazy(() => import('./pages/CartoesCredito'));
function App() { function App() {
const isLoggedIn = authStore((state) => state.isLoggedIn); const isLoggedIn = authStore((state) => state.isLoggedIn);
const setLoggedIn = authStore((state) => state.setLoggedIn);
// Initialize authStore state once at mount time
useEffect(() => {
const storedAuth = localStorage.getItem('autenticado') === 'true';
setLoggedIn(storedAuth);
}, [setLoggedIn]);
return ( return (
<Router> <Router>
<Toaster /> <Toaster />
<Routes> <Routes>
<Route path="/auth" element={isLoggedIn ? <Navigate to="/" /> : <Auth />} /> {/* The auth route should not be inside ProtectedRoute */}
<Route path="/" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Dashboard /></Suspense></ProtectedRoute>} /> <Route
<Route path="/transacoes" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Transacoes /></Suspense></ProtectedRoute>} /> path="/auth"
element={isLoggedIn ? <Navigate to="/" replace /> : <Auth />}
/>
{/* Protected routes */}
<Route path="/" element={
<ProtectedRoute>
<Suspense fallback={<div className="flex items-center justify-center min-h-screen">Carregando...</div>}>
<Dashboard />
</Suspense>
</ProtectedRoute>
} />
<Route path="/transacoes" element={
<ProtectedRoute>
<Suspense fallback={<div className="flex items-center justify-center min-h-screen">Carregando...</div>}>
<Transacoes />
</Suspense>
</ProtectedRoute>
} />
<Route path="/cartoes" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><CartoesCredito /></Suspense></ProtectedRoute>} /> <Route path="/cartoes" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><CartoesCredito /></Suspense></ProtectedRoute>} />
<Route path="/categorias" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Categorias /></Suspense></ProtectedRoute>} /> <Route path="/categorias" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Categorias /></Suspense></ProtectedRoute>} />
<Route path="/metas" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Metas /></Suspense></ProtectedRoute>} /> <Route path="/metas" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Metas /></Suspense></ProtectedRoute>} />
<Route path="/calendario" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Calendario /></Suspense></ProtectedRoute>} /> <Route path="/calendario" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Calendario /></Suspense></ProtectedRoute>} />
<Route path="/whatsapp" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><WhatsApp /></Suspense></ProtectedRoute>} /> <Route path="/whatsapp" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><WhatsApp /></Suspense></ProtectedRoute>} />
<Route path="/grupos-whatsapp" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><GruposWhatsApp /></Suspense></ProtectedRoute>} /> <Route path="/grupos-whatsapp" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><GruposWhatsApp /></Suspense></ProtectedRoute>} />
<Route path="*" element={<Suspense fallback={<div>Carregando...</div>}><NotFound /></Suspense>} />
{/* Not found route */}
<Route path="*" element={
<Suspense fallback={<div className="flex items-center justify-center min-h-screen">Carregando...</div>}>
<NotFound />
</Suspense>
} />
</Routes> </Routes>
</Router> </Router>
); );

View File

@ -3,61 +3,57 @@ import { ReactNode, useEffect, useState } from 'react';
import { Navigate, useLocation } from 'react-router-dom'; import { Navigate, useLocation } from 'react-router-dom';
import { useToast } from "@/components/ui/use-toast"; import { useToast } from "@/components/ui/use-toast";
import LoadingState from '../whatsapp/LoadingState'; import LoadingState from '../whatsapp/LoadingState';
import { authStore } from '@/stores/authStore';
interface ProtectedRouteProps { interface ProtectedRouteProps {
children: ReactNode; children: ReactNode;
} }
const ProtectedRoute = ({ children }: ProtectedRouteProps) => { const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [shouldShowToast, setShouldShowToast] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
const location = useLocation(); const location = useLocation();
const setLoggedIn = authStore((state) => state.setLoggedIn);
// Use a single state variable to track authentication status
const [authStatus, setAuthStatus] = useState<'loading' | 'authenticated' | 'unauthenticated'>('loading');
useEffect(() => { useEffect(() => {
// Check authentication status only once on mount const checkAuthentication = () => {
const checkAuthentication = async () => {
try { try {
setIsLoading(true);
// Check if we have stored authentication status // Check if we have stored authentication status
const storedAuth = localStorage.getItem('autenticado') === 'true'; const storedAuth = localStorage.getItem('autenticado') === 'true';
const userId = localStorage.getItem('userId'); const userId = localStorage.getItem('userId');
if (storedAuth && userId) { if (storedAuth && userId) {
console.log('Usando autenticação armazenada: autenticado'); console.log('Usando autenticação armazenada: autenticado');
setIsAuthenticated(true); setAuthStatus('authenticated');
setLoggedIn(true);
} else { } else {
console.log('Nenhuma sessão encontrada, redirecionando para login'); console.log('Nenhuma sessão encontrada, redirecionando para login');
setIsAuthenticated(false); setAuthStatus('unauthenticated');
setShouldShowToast(true); // Mark that we should show toast, but don't do it here setLoggedIn(false);
// Only show toast once when transitioning to unauthenticated
toast({
title: "Autenticação necessária",
description: "Por favor, faça login para acessar esta página"
});
} }
} catch (error) { } catch (error) {
console.error('Erro ao verificar autenticação:', error); console.error('Erro ao verificar autenticação:', error);
setIsAuthenticated(false); setAuthStatus('unauthenticated');
setShouldShowToast(true); setLoggedIn(false);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
checkAuthentication(); checkAuthentication();
}, []); // Execute only on mount }, [toast, setLoggedIn]); // Only check once on mount, with stable dependencies
// Show toast in a separate useEffect to avoid re-render loops
useEffect(() => {
if (shouldShowToast) {
toast({
title: "Autenticação necessária",
description: "Por favor, faça login para acessar esta página"
});
setShouldShowToast(false); // Reset the flag after showing toast
}
}, [shouldShowToast, toast]);
// Se estiver carregando, mostrar estado de carregamento // Se estiver carregando, mostrar estado de carregamento
if (isLoading) { if (isLoading || authStatus === 'loading') {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
<LoadingState message="Verificando autenticação..." /> <LoadingState message="Verificando autenticação..." />
@ -65,8 +61,8 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
); );
} }
// Se não estiver autenticado, redirecionar para a página de login // Se não estiver autenticado, redirecionar para a página de login - use replace para evitar histórico
if (!isAuthenticated) { if (authStatus === 'unauthenticated') {
return <Navigate to="/auth" state={{ from: location }} replace />; return <Navigate to="/auth" state={{ from: location }} replace />;
} }

View File

@ -6,7 +6,16 @@ interface AuthState {
setLoggedIn: (status: boolean) => void; setLoggedIn: (status: boolean) => void;
} }
// Initialize with the persisted value from localStorage
const getInitialState = (): boolean => {
try {
return localStorage.getItem('autenticado') === 'true';
} catch (e) {
return false;
}
};
export const authStore = create<AuthState>((set) => ({ export const authStore = create<AuthState>((set) => ({
isLoggedIn: localStorage.getItem('autenticado') === 'true', isLoggedIn: getInitialState(),
setLoggedIn: (status) => set({ isLoggedIn: status }), setLoggedIn: (status) => set({ isLoggedIn: status }),
})); }));