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='/' />;
};