3

In my React App, when the user logins, a token is generated (JWT) after that it is stored in LocalStorage.

Once everything is done, in order to maintain user login for different routes, I'm using useContext.

I want to show logout button if the user is logged in (in reality it appears, but after 2 seconds) in short, I could see login / register button and after 2 seconds it shows the logout button.

Here is my code:

Main.js

export default function Main() {

const [userData,setUserData] = useState(
  {token:undefined,
  user:undefined,}
);

useEffect(()=>{
const checkLoggedIn = async() =>{
  const token = localStorage.getItem("auth-token");
  if(token===null) {localStorage.setItem("auth-token","");
  token = "";}
  const tokenRes = await Axios.post("http://localhost:5000/user/tokenIsValid",
  null,
  {headers:{"x-auth-token":token}})
if(tokenRes.data){
  const userRes = await Axios.get("http://localhost:5000/user/",{headers:{"x-auth-token":token}})
  setUserData({
    token,
    user:userRes.data
  })
}
  };

  checkLoggedIn();
}

,[])



  return (
    <BrowserRouter>
      <UserContext.Provider value={{userData,setUserData}}>
      <ThemeProvider theme={theme}>
      
        <Navbar />

        <Switch>
          <Route exact path="/" component={Home} />

          <Route exact path="/login" component={Login} />
          <Route exact path="/register" component={Register} />
                  <Route exact path="/recover" component={Recover} />
          <Route exact path="/privacy-policy" component={PrivacyPolicy} />
          <Route
            exact
            path="/terms-of-service"
            component={TermsAndConditions}
          />
          <Route exact path="/contact" component={Contact} />
          <Route exact path="/faqs" component={FAQs} />
          <Route component={Error} />
        </Switch>

        <myFooter />
        </ThemeProvider>
        </UserContext.Provider>
      
    </BrowserRouter>
  );
}

Then here is my Navbar.js

export default function Navbar(){
  const { userData,setUserData } = useContext(UserContext);
  const classes = useStyles();
  const logout = () =>{
    setUserData({
      token:undefined,
      user:undefined
    })
    localStorage.setItem("auth-token","")
  }
  return(
<>

<AppBar position="static" color="transparent">
  <Toolbar>
    <Typography  className={classes.doBold}>
      <Link to="/" className={classes.noLinkStyle}><img src={require('../images/logo.png')} height="auto" width="150px" className={classes.AlignVertical} alt="Logo"/></Link>
    </Typography>
    <ButtonGroup size="small" variant="outlined" color="secondary">
      {
        userData.user!==undefined?<myLoginMenu  style={{marginRight:'20px'}}/>:<> <Link to="/login" className={classes.noLinkStyle}><myButton variant="contained" content="Login" icon="login"/></Link>
        <Link to="/register" className={classes.noLinkStyle}><myButton variant="contained" primary content="Register" icon="register" /></Link></>
      }



</ButtonGroup>
  </Toolbar>

</AppBar>

</>
  )
}

And at last UserContext.js

import {createContext} from 'react';
export default createContext(null);

And here is my Login Route

router.get("/login", async (req, res) => {
  try {
    const { email, password } = req.body;
    if (!email || !password) {
      return res.status(400).json({ msg: "Something is missing." });
    }
    const student = await Student.findOne({ email: email });
    if (!student)
      return res
        .status(400)
        .json({ msg: "No account reigstered with this email." });

    const isMatch = await bcrypt.compare(password, student.password);
    if (isMatch) {
      
      const token = jwt.sign({id:student._id},process.env.JWT_TOKEN)
      res.json({
          token,
          user:{
              id:student._id
          }
      })
    } else return res.status(400).json({ msg: "Incorrect Password" });
  } catch (error) {
    res.status(500).json({ errorReceived: error });
  }
});
Fara
  • 56
  • 4
  • I think you'll probably have to show us the `Login` component as well if I fully understand your problem. Basically after you login it shifts two a different page but the UI doesn't reflect that you've logged in until two seconds later, I'm assuming. I can not tell for sure without seeing the Login component though. – Cory Harper Dec 08 '20 at 05:23
  • As for it rendering as not logged in if a user is already logged in until two seconds later, that's possibly because you have several asynchronous functions and methods running (useEffect and various API calls), but you do not block rendering until after they are done retrieving some information, resulting in the visual bug. – Cory Harper Dec 08 '20 at 05:25
  • Hi @CoryHarper I've just added the login through /login route (post request). I've updated the question with the new code. Please check. – Fara Dec 08 '20 at 06:16
  • Sorry, I meant the React Login component. – Cory Harper Dec 08 '20 at 06:32

1 Answers1

2

Edited: Sorry I didn't read your question well. Yes, what you're facing is quite normal because at the first render, you're not logged in so the Log out button would appear to be Log in. What you can do is:

  1. Add a server-side renderer. So when your application run on server-side it runs the authentication to get the data of user then returns to client side.
  2. Hide (or animate the Log in/Log out button until the useEffect finishes running) so the user's experience would be more smooth.
Pho Huynh
  • 1,497
  • 3
  • 13
  • 23
  • After I added this line, it is showing userRes is not defined. – Fara Dec 08 '20 at 06:28
  • I will go with SSR but the video I saw on YouTube was following the same pattern as I am doing. In his video, the login/register button is working fine as expected. But not in my case. – Fara Dec 08 '20 at 06:56
  • can you send me the link of that video? – Pho Huynh Dec 08 '20 at 06:57
  • Video 1: https://www.youtube.com/watch?v=sWfD20ortB4 -> For context. Video 2: https://www.youtube.com/watch?v=bppuE6rbO1c -> For show hide login/register. – Fara Dec 08 '20 at 07:01