Can’t think of a better title that describes by dilemma but here it is:
The app uses the the existence of the user in the auth context to determine if they are logged in. This state is set in onAuthStateChanged on login. This works fine as is, however, I want to be able to handle errors in case my API returns one and display it on the login page.
On the login page, after a successful login, the user is navigated to the dashboard page. However, if there is an error in the onAuthStateChanged from getApiUser or createApiUser, this does not get caught because the error is thrown outside the login function. So the login succeeds and navigate is called. I have a component that detects if the user is null, so they are just redirected back to the login page in this case. However, they would not see an error and would be confused why the login did not work.
// LoginPage.tsx
export function LoginPage() {
const navigate = useNavigate();
const { login } = useContext(AuthContext);
const [error, setError] = useState('');
const [email, setEmail] = useState('');
const [pass, setPass] = usestate('');
const handleLoginClick = () => {
try {
await login(email, pass);
navigate('/dashboard');
} catch (err) {
setError(err.message);
}
}
return (
<div>
{error && <div>{error}</div>}
..
)
}
// authContextProvider.tsx
...
const [user, setUser] = useState<ApiUser | null>(null);
const login = async (email, pass) => {
await signInWithEmailAndPassword(auth, email, pass);
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
if (!firebaseUser) {
setUser(null);
} else {
try {
// Fetch user from API.
const apiUser = await getApiUser(firebaseUser.uid);
if (apiUser) {
setUser(apiUser);
} else if (firebaseUser.emailVerified) {
// This is a new user. Create them in the API.
const newUser = await createApiUser(firebaseUser.uid);
setUser(newUser);
} else {
// This user has not yet been verified.
setUser(null);
}
} catch (err) {
setUser(null);
await logout();
}
}
});
return () => unsubscribe();
}, []);