Fix React error #130 - Remove authentication race condition

- Remove setTimeout delays in AdminAuthContext login function
- Make authentication state updates synchronous
- Replace setTimeout navigation with state-based navigation in AdminLoginPage
- Add proper error handling and component lifecycle management in CreateEventPage
- Prevent navigation if component unmounts during async operations

This fixes the issue where users would see React error #130 during login
and couldn't create events or save settings.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-07 13:12:39 +02:00
parent 9dd643338b
commit 2e10374e2c
3 changed files with 40 additions and 26 deletions
+13 -10
View File
@@ -36,13 +36,19 @@ export const AdminAuthProvider: React.FC<AdminAuthProviderProps> = ({ children }
useEffect(() => { useEffect(() => {
// Check if user has a valid token on mount // Check if user has a valid token on mount
const checkAuth = async () => { const checkAuth = async () => {
const token = getAuthToken(true); try {
if (token) { const token = getAuthToken(true);
// For now, just assume the token is valid if (token) {
// TODO: Validate token with backend and get user info // For now, just assume the token is valid
setIsAuthenticated(true); // TODO: Validate token with backend and get user info
setIsAuthenticated(true);
}
} catch (error) {
console.error('Auth check error:', error);
setError('Failed to check authentication');
} finally {
setIsLoading(false);
} }
setIsLoading(false);
}; };
checkAuth(); checkAuth();
@@ -52,10 +58,7 @@ export const AdminAuthProvider: React.FC<AdminAuthProviderProps> = ({ children }
// Token is already stored in cookie by authService // Token is already stored in cookie by authService
setUser(user); setUser(user);
setError(null); setError(null);
// Small delay to ensure state is properly updated setIsAuthenticated(true);
setTimeout(() => {
setIsAuthenticated(true);
}, 100);
}; };
const logout = () => { const logout = () => {
+6 -11
View File
@@ -1,5 +1,5 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useNavigate, Navigate } from 'react-router-dom'; import { Navigate } from 'react-router-dom';
import { Lock, Mail, Eye, EyeOff, AlertCircle } from 'lucide-react'; import { Lock, Mail, Eye, EyeOff, AlertCircle } from 'lucide-react';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@@ -9,7 +9,6 @@ import { authService } from '../../services/auth.service';
import { getAuthToken } from '../../config/api'; import { getAuthToken } from '../../config/api';
export const AdminLoginPage: React.FC = () => { export const AdminLoginPage: React.FC = () => {
const navigate = useNavigate();
const { isAuthenticated, login } = useAdminAuth(); const { isAuthenticated, login } = useAdminAuth();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
@@ -19,9 +18,10 @@ export const AdminLoginPage: React.FC = () => {
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({}); const [errors, setErrors] = useState<Record<string, string>>({});
const [loginSuccess, setLoginSuccess] = useState(false);
// Redirect if already authenticated // Redirect if already authenticated or login successful
if (isAuthenticated) { if (isAuthenticated || loginSuccess) {
return <Navigate to="/admin/dashboard" replace />; return <Navigate to="/admin/dashboard" replace />;
} }
@@ -58,10 +58,7 @@ export const AdminLoginPage: React.FC = () => {
const response = await authService.adminLogin(formData); const response = await authService.adminLogin(formData);
login(response.token, response.user); login(response.token, response.user);
toast.success('Login successful!'); toast.success('Login successful!');
// Add a small delay to ensure auth state is updated setLoginSuccess(true);
setTimeout(() => {
navigate('/admin/dashboard');
}, 200);
} catch (error: any) { } catch (error: any) {
console.error('Login error:', error); console.error('Login error:', error);
@@ -71,9 +68,7 @@ export const AdminLoginPage: React.FC = () => {
const token = getAuthToken(true); const token = getAuthToken(true);
if (token) { if (token) {
// Login was successful, just had a connection issue // Login was successful, just had a connection issue
setTimeout(() => { setLoginSuccess(true);
navigate('/admin/dashboard');
}, 200);
return; return;
} }
toast.error('Network error. Please check your connection and try again.'); toast.error('Network error. Please check your connection and try again.');
+21 -5
View File
@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
Calendar, Calendar,
@@ -45,6 +45,13 @@ const COLOR_THEMES = [
export const CreateEventPage: React.FC = () => { export const CreateEventPage: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const isMountedRef = useRef(true);
useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
const [formData, setFormData] = useState<FormData>({ const [formData, setFormData] = useState<FormData>({
event_type: 'wedding', event_type: 'wedding',
@@ -65,18 +72,27 @@ export const CreateEventPage: React.FC = () => {
const createMutation = useMutation({ const createMutation = useMutation({
mutationFn: eventsService.createEvent, mutationFn: eventsService.createEvent,
onSuccess: (data) => { onSuccess: (data) => {
toast.success('Event created successfully!'); if (isMountedRef.current) {
navigate(`/admin/events/${data.id}`); toast.success('Event created successfully!');
navigate(`/admin/events/${data.id}`);
}
}, },
onError: (error: any) => { onError: (error: any) => {
if (error.response?.data?.errors) { if (!isMountedRef.current) return;
if (error.code === 'ERR_NETWORK' || error.code === 'ERR_CONNECTION_RESET') {
toast.error('Network error. Please check your connection and try again.');
} else if (error.response?.data?.errors) {
const newErrors: Record<string, string> = {}; const newErrors: Record<string, string> = {};
error.response.data.errors.forEach((err: any) => { error.response.data.errors.forEach((err: any) => {
newErrors[err.path] = err.msg; newErrors[err.path] = err.msg;
}); });
setErrors(newErrors); setErrors(newErrors);
} else if (error.response?.status === 401) {
toast.error('Session expired. Please login again.');
navigate('/admin/login');
} else { } else {
toast.error('Failed to create event'); toast.error(error.response?.data?.error || 'Failed to create event');
} }
}, },
}); });