Home > Software engineering >  I am getting redirected to main page after page refresh even if the user is authenticated in private
I am getting redirected to main page after page refresh even if the user is authenticated in private

Time:03-08

I am working on a private route component that should block un authenticated users and redirect them back to home page, it works fine until I refresh, when I refresh the page the page the current user var get null for one second then it gets the value thus triggering the redirect to main, the problem is even time a user refresh page hi will be refreshed to main and he will have to sign up again.. even though he is signed in

// Auth context
 const history = useHistory();
  const location = useLocation();
  const [currentUser, setCurrentUser] = useState<null | any>(null);
  const [currentClient, setCurrentClient] = useState<null | any>(null);
  const [loading, setLoading] = useState<boolean>(false);

  const context = useContext(exercisesContext);
  const _miscContext = useContext(miscContext);

  console.log(_miscContext);
  useEffect(() => {
    setLoading(true);
    //  with unsubscribe logic !!
    //const unsubscribe =
    onAuthStateChanged(auth, (user: any) => {
      console.log(user, 'test................................');
      // if (!user?.email_verified) {
      //  _miscContext.SetSnackBarMsg(true, 'Please verify your email');
      //  return;
      // }
      setCurrentUser(user);
      setLoading(false);
    });
    //return unsubscribe();
  }, []);

  const Signin = (email: string, password: string) => {
    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        const { user } = userCredential;
        console.log(userCredential.user.emailVerified, 'pppppppp');
        if (!user.emailVerified) {
          _miscContext.SetSnackBarMsg(true, 'Please verify your email');
          return;
        }
        // Signed in
        //const user = userCredential.user;
        //if (user) {
        history.push('/createtraining');
        // }
      })
      .catch((error) => {
        const errorCode = error.code;
        console.log(errorCode);
      });
  };

  const Signup = async (email: string, password: string, userType: string) => {
    await createUserWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        //email_verified;

        setDoc(doc(db, 'Data', userCredential.user.uid), {
          createdAt: Timestamp.now(),
        });

        sendEmailVerification(userCredential.user);
        _miscContext.SetSnackBarMsg(true, 'Please check your email');
      })
      .catch((error) => {
        const errorCode = error.code;
        //const errorMessage = error.message;
        console.log(errorCode);
        if (errorCode === 'auth/weak-password') {
          _miscContext.SetSnackBarMsg(true, 'Password must be at least 6 charechters.');
          history.push('/');
        }
        if (errorCode === 'auth/email-already-in-use') {
          _miscContext.SetSnackBarMsg(true, 'Email already exists.');
          history.push('/');
        }
      });
    _miscContext.SetModal(true, '', 'unit');
  };






  return (
    <authContext.Provider
      value={{
        currentUser,
        currentClient,
        loading,
        Signin,
        Signup,
      }}
    >
      {children}
    </authContext.Provider>
  );
};

// Privite route component 
import React, { useContext } from 'react';
import { Redirect, Route } from 'react-router-dom';
import { authContext } from '../context/auth/AuthState';

interface Props {
  component: any;
  exact: any;
  path: any;
}

export const PrivateRoute: React.FC<Props> = ({ component: Componenet, ...rest }) => {
  const _authContext = useContext(authContext);
  const { currentUser, loading } = _authContext;
  console.log(currentUser);

  return <Route {...rest} render={(props) => (!currentUser && !loading ? <Redirect to='/' /> : <Componenet />)} />;
};

CodePudding user response:

Issue

The issue is that both your initial currentUser and loading state are falsey.

const [currentUser, setCurrentUser] = useState<null | any>(null);
const [loading, setLoading] = useState<boolean>(false);

This means when the app is initially loading/mounting, if the user is attempting to access a protected route they are redirected before the firsbase auth check has had a chance to complete.

export const PrivateRoute: React.FC<Props> = ({ component: Component, ...rest }) => {
  const _authContext = useContext(authContext);
  const { currentUser, loading } = _authContext;
  console.log(currentUser);

  return (
    <Route
      {...rest}
      render={(props) => (!currentUser && !loading
        ? <Redirect to='/' />
        : <Component />
      )}
    />
  );
};

Solution

One possible solution is to start with an initially loading state of true or an indeterminant currentUser value that doesn't equal your verified unauthenticated state, and to use an explicit check for either before committing to redirecting or rendering the protected component.

Example:

const [currentUser, setCurrentUser] = useState<null | any>();
const [loading, setLoading] = useState<boolean>(true);

...

export const PrivateRoute: React.FC<Props> = (props) => {
  const _authContext = useContext(authContext);
  const { currentUser, loading } = _authContext;
  console.log(currentUser);

  if (currentUser === undefined || loading) {
    return null; // or loading spinner, etc...
  }

  return currentUser
    ? <Route {...props} />
    : <Redirect to='/' />;
};
  • Related