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 { 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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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 />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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 }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user