This is my Login Component:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import {signInWithEmailAndPassword} from "firebase/auth";
import {auth} from "./firebase.js"
export default LoginComponent = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
signInWithEmailAndPassword(auth, email, password).then(() => {
navigate('/')
}).catch((error) => {
});
};
My AuthProvider:
import {onAuthStateChanged,} from "firebase/auth";
import { createContext, useEffect, useState } from "react";
import PropTypes from "prop-types";
import {auth} from "./firebase.js";
export const AuthContext = createContext(null);
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [accessToken, setAccessToken] = useState(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
if (currentUser) {
setAccessToken(await currentUser.getIdToken(true));
}
setUser(currentUser);
setLoading(false);
});
return () => unsubscribe();
}, []);
const authValue = {
user,
loading,
accessToken,
};
return <AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>;
};
AuthProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default AuthProvider;
On my routes page, I am doing this:
import { useContext } from 'react'
import { AuthContext } from "./components/Auth/AuthProvider.js";
import UserProfile from "./components/UserProfile"
const App = () => {
const { user, loading } = useContext(AuthContext);
function LandingPageRedirect({ children }) {
if (loading) {
return <div>Loading...</div>;
}
if (user) {
return <>{children}</>
} else {
return (
<Navigate
to="/landing"
state={{
from: location.pathname,
}}
replace
/>
)
}
}
return (
<div>
<BrowserRouter>
<Routes>
<Route path='/' element={
<LandingPageRedirect>
<Home />
</LandingPageRedirect>
}
/>
However, after sign in I am being sent back to"/landing" rather than "/home". I need to refresh the page in order to be sent to the home page rather than landing page. How do I fix this?
Possibly you navigate to '/' before you set user to state. It can happen because of waiting while promise from getIdToken be resolved or even authStateChange call happens after navigation. I have many solutions for you which you can try. Firstly let's move setUser before that getToken call in case it doesn't help you will find another solution below.
Like this:
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
setUser(currentUser);
if (currentUser) {
setAccessToken(await currentUser.getIdToken(true));
}
setLoading(false);
});
return () => unsubscribe();
}, []);
or rewrite it without await.
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
currentUser
.getIdToken(true)
.then((token) => {
setAccessToken(token);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
setLoading(false);
});
});
return () => unsubscribe();
}, []);
Or divide this logic to two different useEffect function calls:
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
setLoading(false);
});
return () => unsubscribe();
}, []);
useEffect(() => {
if (currentUser) {
currentUser
.getIdToken(true)
.then((token) => {
setAccessToken(token);
})
.catch((error) => {
console.log(error);
});
}
}, [currentUser]);
In case it doesn't work stick with next solution I suggested.
As you can see in the docs https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#signinwithemailandpassword
signinwithemailandpassword returns Promise that has user inside
https://firebase.google.com/docs/reference/js/v8/firebase.auth#usercredential
So you can setUser in then block.
So expose your setUser from the authProvider. Modified code:
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [accessToken, setAccessToken] = useState(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
if (currentUser) {
setAccessToken(await currentUser.getIdToken(true));
}
setUser(currentUser);
setLoading(false);
});
return () => unsubscribe();
}, []);
const authValue = {
user,
loading,
accessToken,
setUser
};
return <AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>;
};
Update your handleSubmit
const { setUser } = useContext(AuthContext);
...
const handleSubmit = (e) => {
e.preventDefault();
signInWithEmailAndPassword(auth, email, password).then((res) => {
setUser(res.user);
navigate('/')
}).catch((error) => {
});
};
After that the application won't redirect you to landing.