After I click login, the home page renders but without a user, so the profile component doesn't render. The backend works properly. Is there something wrong with my AuthContext.jsx?
// src/context/AuthContext.jsx
import { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import api from '../api/axios';
// Create AuthContext with default values
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [token, setToken] = useState(() => localStorage.getItem('token'));
const [loading, setLoading] = useState(true);
const [initialized, setInitialized] = useState(false);
const navigate = useNavigate();
console.log("AuthContext state", { user, token, initialized, loading });
useEffect(() => {
const initializeAuth = async () => {
if (!token) {
setLoading(false);
setInitialized(true);
return;
}
// Skip if user is already loaded (prevents duplicate calls from login)
if (user && initialized) {
setLoading(false);
return;
}
try {
const { data } = await api.get('/api/users/me', {
headers: { Authorization: `Bearer ${token}` }
});
setUser(data);
console.log("User loaded from token:", data);
} catch (error) {
console.error('Error fetching user on init:', error);
setUser(null);
setToken(null);
localStorage.removeItem('token');
} finally {
setInitialized(true);
setLoading(false);
}
};
initializeAuth();
}, [token]);
// Login - fetch user data immediately after setting token
const login = async (email, password) => {
try {
setLoading(true);
const { data } = await api.post('/api/auth/login', { email, password });
const newToken = data.token;
localStorage.setItem('token', newToken);
setToken(newToken);
// Immediately fetch user data with the new token
const userData = await api.get('/api/users/me', {
headers: { Authorization: `Bearer ${newToken}` }
});
setUser(userData.data);
setInitialized(true);
console.log("Login successful, user loaded:", userData.data);
// Navigate after user is set
navigate('/');
return data;
} catch (error) {
console.error('Login error:', error);
// Clean up on error
localStorage.removeItem('token');
setToken(null);
setUser(null);
throw error;
} finally {
setLoading(false);
}
};
// Logout
const logout = () => {
localStorage.removeItem('token');
setToken(null);
setUser(null);
setInitialized(false);
navigate('/login');
};
// Sync across tabs
useEffect(() => {
const handleStorageChange = ({ key, newValue }) => {
if (key === 'token') {
setToken(newValue);
if (!newValue) setUser(null);
}
};
window.addEventListener('storage', handleStorageChange);
return () => window.removeEventListener('storage', handleStorageChange);
}, []);
return (
<AuthContext.Provider value={{ user, token, loading, initialized, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// Hook to use Auth context
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error('useAuth must be used within an AuthProvider');
return context;
}
you should extract the token like so:
const response = await api.post('/api/auth/login', { email, password }); const newToken = response.data.data.token;
const login = async (email, password) => {
setLoading(true);
try {
const response = await api.post('/api/auth/login', { email, password });
const newToken = response.data.data.token;
localStorage.setItem('token', newToken);
setToken(newToken);
const { data: userProfile } = await api.get('/api/users/me', {
headers: { Authorization: `Bearer ${newToken}` }
});
setUser(userProfile);
setInitialized(true);
navigate('/');
return response.data;
} catch (error) {
localStorage.removeItem('token');
setToken(null);
setUser(null);
throw error;
} finally {
setLoading(false);
}
};