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:
parent
36a045d899
commit
63fd48a4a9
44
src/App.tsx
44
src/App.tsx
@ -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>
|
||||
);
|
||||
|
||||
@ -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 />;
|
||||
}
|
||||
|
||||
|
||||
@ -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 }),
|
||||
}));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user