0

When a user is signed in, the user can goto route /profile and visit profile page.

<Route exact path={`${path}/profile`} 
       component={UserProfilePage}
/>

Whis is this component:

import { Switch, Route, useHistory, useRouteMatch } from 'react-router-dom';
import UserProfile from "../components/Profile/UserProfile";
import PrivateRoute from '../components/Auth/PrivateRoute';


const UserProfilePage = () => {
    
    const { path } = useRouteMatch();
    console.log(path)
    
    return (
        <Switch>
            <PrivateRoute path={`${path}`} component={UserProfile} exact/>
        </Switch>   
    )
}

export default UserProfilePage;

This is my private route:

import React, { useContext } from "react";
import { Route, Redirect, useRouteMatch, useLocation } from "react-router-dom";
import AuthFBContext from "../../store/auth-contextFB";
import LangContext from "../../store/lang-context";

export default function PrivateRoute({ component: Component, ...rest }) {
  
    const authFBCtx = useContext(AuthFBContext);
    const langCxt = useContext(LangContext);

    const { path } = useRouteMatch();


  return (
    <Route
      {...rest}
      
      render={props => {
        return authFBCtx.currentUser ? 
        <Component {...props} /> : <Redirect to={`/${langCxt.lang}/auth/signin`} />
      }}
    ></Route>
  );
};

When I goto this page via Link component without reload of the tab, it works fine. but when the browswer is reloaded even with the user authenticated, it redirects to sign in page.

Why is this?

Here is my providers:

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
        
        setCurrentUser(user);
        console.log('currentuser:');
        console.log(currentUser);

        dBCtx.getUserData1(user.uid)
        .then((result)=>{

          if(result === false) {
            console.log('creating a user profile for' + user.uid)
            dBCtx.createUser(user.displayName, user.email, user.uid);
          }
        })
        .catch(err => { console.log(err) })

        // ...
      } else {
        console.log('not logged in')

      }
    });
  }, [currentUser])

    const contextValue = {
    currentUser,
    login,
    googleLogin,
   ...
    };
  
    return (
      <AuthFBContext.Provider value={contextValue}>
        {props.children}
      </AuthFBContext.Provider>
    );
  };
  
  export default AuthFBContext;

masafood
  • 325
  • 2
  • 11
  • Can we look at the providers? – Cotldus Oct 30 '22 at 00:38
  • TL;DR The issue is that your initial `currentUser` state appears to match your unauthenticated state and so the `PrivateRoute` component doesn't wait for ***confirmed*** authentication status and bounces off the protected route. – Drew Reese Oct 30 '22 at 14:49
  • @DrewReese hey so i tired with loca.storage.get('some_user_auth', 'someAuth'), and yes It works like its supposed to. It looks like React component gets rendered before Firebase provides auth for the user, how can i change this behaviour? – masafood Oct 31 '22 at 08:34
  • @Cotldus i put up my proviers also but like drew says, i dont think there is anything wrong with my context... – masafood Oct 31 '22 at 08:42
  • @DrewReese so basically this link here: https://github.com/firebase/firebase-js-sdk/issues/462 says it, but not sure how to implement exactly without spending too much time also its a context function so i do not want to mess it up as it will affect the whole app – masafood Oct 31 '22 at 13:17
  • "It looks like React component gets rendered before Firebase provides auth for the user, how can i change this behavior?" You can't really. The auth check occurs as a side-effect via the `useEffect` hook, which runs at the end of a render cycle. The code should handle the indeterminant auth status on the initial renders. – Drew Reese Oct 31 '22 at 15:55
  • The `onAuthStateChanged` will return a user object when a user is authenticated, and it will return `null` when a user is not authenticated. The initial `authFBCtx.currentUser` state/context value should be something, *anything*, ***other than*** either of these two values, e.g. `undefined`. From here you can conditionally render null or a loading indicator to "wait" for the auth status to resolve. Note in the duplicate the `if (authLogin === undefined) return null;` block, this is exactly what it's for. – Drew Reese Oct 31 '22 at 15:55
  • @DrewReese that is what i have switched to now using conditionally rendering. can I use promise to wait on authFBCtx.currentUser and then have react-dom decide on the outcome of the promise? – masafood Oct 31 '22 at 16:05
  • No, react rendering is completely synchronous. You could wrap `onAuthStateChanged` in a Promise but you're effectively going to do the same thing, i.e. conditionally render the `Outlet` or `Navigate` components ***after*** the Promise has resolved. It's an unnecessary complication IMO when the `onAuthStateChanged` callbacks already run asynchronously and you can update the user state when the auth status changes. – Drew Reese Oct 31 '22 at 16:07
  • @DrewReese, so this is the best i can do like this? if(!authFBCtx.currentUser) { return() } return ( ) – masafood Oct 31 '22 at 16:09
  • Not quite, something more like `if (authFBCtx.currentUser === undefined) return null;`, and then when the `currentUser` is later updated to be a defined user object or it is null, the rest of the `PrivateRoute` component is returned, i.e. either the protected route or the redirect. – Drew Reese Oct 31 '22 at 16:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/249207/discussion-between-masafood-and-drew-reese). – masafood Oct 31 '22 at 19:48

0 Answers0