Home > front end >  Firebase Authentication using React - issue with page refresh
Firebase Authentication using React - issue with page refresh

Time:01-01

I am trying to create a basic React app with Firebase and Zustand. There is a login page and a dashboard page. You can only view the dashboard page if you are logged in otherwise you get redirected to login page. After logging in, the user is redirected to dashboard page which displays user email id. The problem is when I refresh the dashboard page after logging in, in spite of being logged in, I get redirected to login page because in the first render,username is null.Code is attached. How can I get around this behavior?

function App() {
  const userData = useUserStore((state) => state.setUser);
  useEffect(() => {
    const authState = onAuthStateChanged(auth, (user) => {
      if (user) {
        userData(user.email);
      } else {
        console.log("no user");
      }
    });

    return () => {
      authState();
    };
  }, []);

  return <RouterProvider router={router} />;
}
function Dashboard() {
  const username = useUserStore((state) => state.username);

  return username ? (
    <div>Hi, {username}</div>
  ) : (
    <Navigate to="/login" />
  );
}

CodePudding user response:

There are a few ways you could handle this issue. One option is to use a useEffect hook in the Dashboard component to check if the user is logged in on every render. You can do this by using the onAuthStateChanged method from Firebase to check if the user is logged in. If the user is logged in, you can do nothing. If the user is not logged in, you can use the Navigate component from the @reach/router library to redirect the user to the login page.

Here's some example code for how you could implement this:

function Dashboard() {
  const username = useUserStore((state) => state.username);

  useEffect(() => {
    const authState = onAuthStateChanged(auth, (user) => {
      if (!user) {
        Navigate("/login");
      }
    });

    return () => {
      authState();
    };
  }, [username]); // Only run this effect when the username changes

  return username ? (
    <div>Hi, {username}</div>
  ) : (
    <div>Loading...</div>
  );
}

Another option is to use the useSession hook from the firebase/auth library to check if the user is logged in. This hook will automatically check the user's login status on every render and update the login state accordingly. You can use this hook in the App component and pass the login state down to the Dashboard component as a prop.

Here's some example code for how you could implement this:

function App() {
  const { user } = useSession();

  return (
    <RouterProvider router={router}>
      <Dashboard user={user} />
    </RouterProvider>
  );
}

function Dashboard({ user }) {
  return user ? (
    <div>Hi, {user.email}</div>
  ) : (
    <Navigate to="/login">Hello</Navigate>
  );
}

CodePudding user response:

Yes, you're right, adding the useEffect hook to every authenticated route would result in a lot of code repetition. One way to avoid this repetition is to create a higher-order component (HOC) that handles the authentication check for you.

An HOC is a function that takes a component as an argument and returns a new component with some additional behavior. In this case, you can create an HOC that checks if the user is logged in and redirects the user to the login page if they are not. You can then use this HOC to wrap any routes that require authentication, and the authentication check will be handled automatically.

Here's some example code for how you could implement this:

// auth-hoc.js
import { useEffect } from "react";
import { Navigate } from "@reach/router";
import { onAuthStateChanged } from "firebase/auth";

export default function withAuth(Component) {
  return function AuthenticatedComponent(props) {
    useEffect(() => {
      const authState = onAuthStateChanged(auth, (user) => {
        if (!user) {
          Navigate("/login");
        }
      });

      return () => {
        authState();
      };
    }, []);

    return <Component {...props} />;
  };
}

Then, you can use the HOC like this:

import withAuth from "./auth-hoc";

function Dashboard() {
  const username = useUserStore((state) => state.username);

  return username ? (
    <div>Hi, {username}</div>
  ) : (
    <div>Loading...</div>
  );
}

export default withAuth(Dashboard);

Now, any routes that use the withAuth HOC will be protected by the authentication check. This way, you can avoid having to repeat the same code in multiple places.

  • Related