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:
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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.');
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user