Reputation: 1829
I'm using Firebase v9 and react-router v6. I haven't used v6 so this was quite confusing. How can I make it where the guest user can only access the login page. Only users who were logged in can access the homepage and other pages.
Everytime I'll reload any page, it will show this in the console but it will still direct the user to the right page :
No routes matched location "/location of the page"
How can I use a private route for the profile page?
//custom hook
export function useAuth() {
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => setCurrentUser(user));
return unsub;
}, []);
return currentUser;
}
App.js
import { auth, useAuth } from "./Firebase/utils";
import { onAuthStateChanged } from "firebase/auth";
function App() {
const currentUser = useAuth();
const user = auth.currentUser;
const navigate = useNavigate();
console.log(currentUser?.email);
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
console.log(uid);
navigate("/Home");
// ...
} else {
// User is signed out
// ...
navigate("/");
}
});
}, []);
return (
<div>
<div>
<Routes>
{currentUser ? (
<>
//If i do it this way and I'll go the profile page and reload it, it will always go to back to the Homepage.
<Route path="/Home" element={<Home />} />
<Route path="/Profile" element={<ProfilePage />} />
</>
) : (
<>
<Route
path="/"
element={
<LogInPage />
}
/>
</>
)}
</Routes>
</div>
</div>
);
}
export default App;
This is what the console.log(user)
shows:
Package.json file:
Upvotes: 3
Views: 1738
Reputation: 202667
The main issue is that the currentUser
value is initially falsey
const [currentUser, setCurrentUser] = useState();
and you are making a navigation decision on unconfirmed authentication status in App
<Routes>
{currentUser ? (
<>
// If i do it this way and I'll go the profile page and reload it,
// it will always go to back to the Homepage.
<Route path="/Home" element={<Home />} />
<Route path="/Profile" element={<ProfilePage />} />
</>
) : (
<>
<Route
path="/"
element={<LogInPage />}
/>
</>
)}
</Routes>
When refreshing the page the currentUser
state is reset, is undefined, i.e. falsey, and only the "/"
path is rendered.
In react-router-dom
is a common practice to abstract route protection into a specialized "protected route" component. You will also want to conditionally handle the indeterminant state until your Firebase auth check has had a chance to confirm an authentication status and update the currentUser
state.
Example:
export function useAuth() {
const [currentUser, setCurrentUser] = useState(); // <-- initially undefined
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => setCurrentUser(user)); // <-- null or user object
return unsub;
}, []);
return { currentUser };
}
AuthWrapper - Uses the useAuth
hook to check the authentication status of user. If currentUser
is undefined it conditionally returns early null
or some other loading indicator. Once the currentUser
state is populated/defined the component conditionally renders either an Outlet
for nested/wrapped Route
components you want to protect, or the Navigate
component to redirect to your auth route.
import { Navigate, Outlet, useLocation } from 'react-router-dom';
const AuthWrapper = () => {
const location = useLocation();
const { currentUser } = useAuth();
if (currentUser === undefined) return null; // <-- or loading spinner, etc...
return currentUser
? <Outlet />
: <Navigate to="/" replace state={{ from: location }} />;
};
App - Unconditionally renders all routes, wrapping the Home
and Profile
routes in the AuthWrapper
layout route.
function App() {
return (
<div>
<div>
<Routes>
<Route element={<AuthWrapper />}>
<Route path="/Home" element={<Home />} />
<Route path="/Profile" element={<ProfilePage />} />
</Route>
<Route path="/" element={<LogInPage />} />
</Routes>
</div>
</div>
);
}
Upvotes: 4