Search code examples
reactjsfirebasereact-router

after reloading the page of a private route, the user is redirected to the login page. [react router]


I made a protected route with react router for authentication. It redirects user to the blogs page after login/register. But when I refresh the blogs page, It takes me again to login page. I want stay on the blogs page after refresh. How should I do it? I used react router hooks, firebase, react bootstrap

<Route
   path="/blogs"
   element={
   <RequireAuth>
     <Blogs></Blogs>
   </RequireAuth>
   }
></Route>

RequireAuth file -

import React from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { Navigate, useLocation } from "react-router-dom";
import auth from "../../firebase.init";

function RequireAuth({ children }) {
  const [user] = useAuthState(auth);
  let location = useLocation();

  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
}

export default RequireAuth;

login page -

import { Link, useLocation, useNavigate } from "react-router-dom";

const Login = () => {
  let navigate = useNavigate();
  let location = useLocation();
  let from = location.state?.from?.pathname || "/";


  const handleSubmit = async (event) => {
    // firebase login code
    navigate(from, { replace: true });
  };

  return (
      <Form onSubmit={handleSubmit}>
        
      </Form>
  );
};

export default Login;

Solution

  • I solved this issue by handling an extra loading state on the RequireAuth component.

    RequireAuth.jsx file

    import React from "react";
    import { useAuthState } from "react-firebase-hooks/auth";
    import { Navigate, useLocation } from "react-router-dom";
    import auth from "../../firebase.init";
    
    function RequireAuth({ children }) {
      const [user, loading] = useAuthState(auth); // change here
      let location = useLocation();
    
      // handling loading state to not redirect after page reload.
      if (loading) {
        return <div> Loading <div/>
      }
    
      if (!user) {
        return <Navigate to="/login" state={{ from: location }} replace />;
      }
    
      return children;
    }
    
    export default RequireAuth;
    

    By adding a loading state it prevents the code from running when the login is still in progress. Because the first time when the page loads useAuthState which is supposed to monitor the user, it returns user = null. That is why this line -

      if (!user) {
        return <Navigate to="/login" state={{ from: location }} replace />;
      }
    

    runs and redirects the user to the login page.

    After the page loads and when useAuthState is again called automatically, it returns the user = user. This means the user is here and it returns children, in my case Blogs component.