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 { Suspense, lazy } from 'react';
import { Suspense, lazy, useEffect } from 'react';
import { Toaster } from "@/components/ui/sonner";
import Auth from './pages/Auth';
import ProtectedRoute from './components/auth/ProtectedRoute';
@ -19,22 +18,55 @@ const CartoesCredito = lazy(() => import('./pages/CartoesCredito'));
function App() {
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 (
<Router>
<Toaster />
<Routes>
<Route path="/auth" element={isLoggedIn ? <Navigate to="/" /> : <Auth />} />
<Route path="/" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Dashboard /></Suspense></ProtectedRoute>} />
<Route path="/transacoes" element={<ProtectedRoute><Suspense fallback={<div>Carregando...</div>}><Transacoes /></Suspense></ProtectedRoute>} />
{/* The auth route should not be inside ProtectedRoute */}
<Route
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="/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="/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="/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>
</Router>
);

View File

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

View File

@ -6,7 +6,16 @@ interface AuthState {
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) => ({
isLoggedIn: localStorage.getItem('autenticado') === 'true',
isLoggedIn: getInitialState(),
setLoggedIn: (status) => set({ isLoggedIn: status }),
}));