skip to Main Content

I have a simple blog site I am trying to create. However, once I login and try to navigate to a different component of the site, I get taken back to the Login page. I think there’s a disconnect as far as how to use useContext correctly. I followed this video and had to stop following along when he started using authService because i wasn’t sure where exactly he was getting it from. MY code:

App.js

import './App.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import LoggedIn from './NavBar/LoggedIn';
import Home from './Component/Home';
import { About } from './Component/About';
import { Blog } from './Component/Blog';
import { Products } from './Component/Products';
import { Pricing } from './Component/Pricing';
import { UserContext } from './utils/UserContext';
import { LoginPage } from './Component/LoginPage';
import { AuthProvider, useAuth } from './utils/UserContext'
import PrivateRoute from './Component/PrivateRoute'

function App() {

  return (
    <>
      <BrowserRouter>
        <AuthProvider>
          <PrivateRoute>
            <LoggedIn />
          </PrivateRoute>
          <Routes>
            <Route path='/' element={<LoginPage />} />
            <Route path='/home' element={<Home />} />
            <Route path='/about' element={<About />} />
            <Route path='/blog' element={<Blog />} />
            <Route path='/products' element={<Products />} />
            <Route path='/pricing' element={<Pricing />} />
          </Routes>
        </AuthProvider>
      </BrowserRouter>
    </>
  );
}

export default App;

UserContext.js

import React, { useState, useEffect, useContext, createContext } from "react";


const UserContext = createContext();

export function useAuth(){
    return useContext(UserContext)
}
export function AuthProvider(props){
    const [authUser, setAuthUser] = useState(null);
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    const value = {
        authUser,
        setAuthUser,
        isLoggedIn,
        setIsLoggedIn
    }

    return <UserContext.Provider value={value}>{props.children}</UserContext.Provider>

LoginPage.js

import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Box from '@mui/material/Box';
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button'
import '../CSS/LoginPageCSS.css'
import { useAuth  } from '../utils/UserContext';

export function LoginPage() {

    const navigate = useNavigate();

    const {
        authUser, setAuthUser, isLoggedIn, setIsLoggedIn
    } = useAuth()

    const [loginAttempts, setLoginAttempts] = useState(3); 

    const handleLogin = (event) => {
        event.preventDefault();
        const data = new FormData(event.currentTarget);
        validateLogin(data.get("username"), data.get("password"));
        // console.log({
        //   email: data.get("username"),
        //   password: data.get("password"),
        // });
    }

    const validateLogin = async (username, password) => {
        const res = await fetch(`http://localhost:8000/api/users/${username}`);
        const data = await res.json();
        const pswrd = data.user.password;

        if (password !== pswrd) {
            setLoginAttempts(loginAttempts => loginAttempts - 1);
            console.log("ATTMEPTS:", loginAttempts, "Password", password, "pswrd:", pswrd);
        } else {
            setIsLoggedIn(true);
            setAuthUser(username);
            navigate('/home');
        }
    }

    return (
        <div>
            {
                loginAttempts > 0 ? (
                    <div className='LoginPage_container'>
                        <div>
                            <h2>Welcome to THE SPACE</h2>
                            <p>Login to get started</p>

                        </div>
                        <Box
                            onSubmit={handleLogin}
                            component="form"
                            sx={{
                                '& .MuiTextField-root': { m: 1, width: '25ch' },
                            }}
                            noValidate
                            autoComplete="off"
                        >
                            <div>
                                <TextField
                                    id="username"
                                    name="username"
                                    label="Username"
                                />
                                <TextField
                                    id="password"
                                    name="password"
                                    label="Password"
                                />
                            </div>
                            <Button
                                name="password"
                                type="submit"
                                variant="contained"
                            >
                                Sign In
                            </Button>
                        </Box>
                    </div>
                ) : <h1>Too many attempts, aborting</h1>
          }
        </div>
    )

}

LoggedIn.js (redirected to after login is verified. A protected NAvBar that is only able to be seen if user is loggedin)

import  React, { useEffect, useState } from "react";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import Menu from "@mui/material/Menu";
import MenuIcon from "@mui/icons-material/Menu";
import Container from "@mui/material/Container";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import Tooltip from "@mui/material/Tooltip";
import MenuItem from "@mui/material/MenuItem";
import AdbIcon from "@mui/icons-material/Adb";
import Badge from "@mui/material/Badge";
import NotificationsIcon from "@mui/icons-material/Notifications";
import { Link } from "react-router-dom";
import { useContext } from 'react';

function LoggedIn( { authUser } ) {
  const style = {
    textDecoration: "none",
    color: "white",
  };
  
  const home = <Link to={`/home`} style={style}>Home</Link>
  const products = <Link to={`/products`} style={style}>Products</Link>
  const pricing = <Link to={`/pricing`} style={style}>Pricing</Link>
  const blog = <Link to={`/blog`} style={style}>Blog</Link>
  const about = <Link to={`/about`} style={style}>About</Link>
  const profile = <Link to={`/profile`} style={style}>Profile</Link>
  const account = <Link to={`/account`} style={style}>Account</Link>
  const dashboard = <Link to={`/dashboard`} style={style}>Dashboard</Link>
  const logout = <Link to={`/logout`} style={style}>Logout</Link>
  
  const pages = [home, products, pricing, blog, about];
  const settings = [home, profile, account, dashboard, logout];

  const [anchorElNav, setAnchorElNav] = React.useState(null);
  const [anchorElUser, setAnchorElUser] = React.useState(null);

  const handleOpenNavMenu = (event) => {
    setAnchorElNav(event.currentTarget);
  };
  const handleOpenUserMenu = (event) => {
    setAnchorElUser(event.currentTarget);
  };

  const handleCloseNavMenu = () => {
    setAnchorElNav(null);
  };

  const handleCloseUserMenu = () => {
    setAnchorElUser(null);
  };

  const [userData, setUserData] = useState('');

    const getUserData = async () => {
        const res = await fetch(`http://localhost:8000/api/users/${authUser}`);
        const data = await res.json();
        const user_data = data.user; 
        
        console.log("DATA:", user_data)

        setUserData(user_data)
    };

    useEffect(() => {
      getUserData();
    }, []);

  return (
    <AppBar position="static">
      <Container maxWidth="xl">
        <Toolbar disableGutters>
          <AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
          <Typography
            variant="h6"
            noWrap
            component="a"
    **HERE IS WHERE IT WONT REDIRECT TO**
            href="/blog"
            sx={{
              mr: 2,
              display: { xs: "none", md: "flex" },
              fontFamily: "monospace",
              fontWeight: 700,
              letterSpacing: ".3rem",
              color: "inherit",
              textDecoration: "none",
            }}
          >
            {userData.user_name}
          </Typography>

          <Box sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}>
            <IconButton
              size="large"
              aria-label="account of current user"
              aria-controls="menu-appbar"
              aria-haspopup="true"
              onClick={handleOpenNavMenu}
              color="inherit"
            >
              <MenuIcon />
            </IconButton>
            <Menu
              id="menu-appbar"
              anchorEl={anchorElNav}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "left",
              }}
              keepMounted
              transformOrigin={{
                vertical: "top",
                horizontal: "left",
              }}
              open={Boolean(anchorElNav)}
              onClose={handleCloseNavMenu}
              sx={{
                display: { xs: "block", md: "none" },
              }}
            >
              {pages.map((page) => (
                <MenuItem key={page} onClick={handleCloseNavMenu}>
                  <Typography textAlign="center">{page}</Typography>
                </MenuItem>
              ))}
            </Menu>
          </Box>
          <AdbIcon sx={{ display: { xs: "flex", md: "none" }, mr: 1 }} />
          <Typography
            variant="h5"
            noWrap
            component="a"
            href=""
            sx={{
              mr: 2,
              display: { xs: "flex", md: "none" },
              flexGrow: 1,
              fontFamily: "monospace",
              fontWeight: 700,
              letterSpacing: ".3rem",
              color: "inherit",
              textDecoration: "none",
            }}
          >
          </Typography>
          <Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
            {pages.map((page) => (
              <Button
                key={page}
                onClick={handleCloseNavMenu}
                sx={{ my: 2, color: "white", display: "block" }}
              >
                {page}
              </Button>
            ))}
          </Box>

          <Box sx={{ flexGrow: 0 }}>
            <Tooltip title="Open settings">
              <IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
                <Avatar
                  alt={userData.user_name}
                  src={userData.profile_pic}
                />
              </IconButton>
            </Tooltip>
            <Menu
              sx={{ mt: "45px" }}
              id="menu-appbar"
              anchorEl={anchorElUser}
              anchorOrigin={{
                vertical: "top",
                horizontal: "right",
              }}
              keepMounted
              transformOrigin={{
                vertical: "top",
                horizontal: "right",
              }}
              open={Boolean(anchorElUser)}
              onClose={handleCloseUserMenu}
            >
              {settings.map((setting) => (
                <MenuItem key={setting} onClick={handleCloseUserMenu}>
                  <Typography textAlign="center">{setting}</Typography>
                </MenuItem>
              ))}
            </Menu>
          </Box>
        </Toolbar>
      </Container>
    </AppBar>
  );
}
export default LoggedIn;

PrivateRoute.js

import React, { useState, cloneElement, useEffect } from 'react'
import { Navigate } from 'react-router-dom'
import { useAuth } from '../utils/UserContext';

const PrivateRoute = ({ children }) => {
    const {
        authUser, setAuthUser, isLoggedIn, setIsLoggedIn
    } = useAuth()

  if (isLoggedIn) return (React.cloneElement(children, {authUser}));

  return <Navigate to='/' />
}

export default PrivateRoute;

I have a feeling I am missing a useEffect somewhere but dont know how or where to implement it. Any help would be greatly appreciated.

2

Answers


  1. Chosen as BEST ANSWER

    Firstly, this article helped a lot. What i eventually ended doing was making the LoggedIn component its own private route and wrapping that around home. That was the work around to get my context passed down and eventually being able to redirrect to another component and context persisting.

    LoggedIn.jsx

    import  React, { useEffect, useState } from "react";
    import AppBar from "@mui/material/AppBar";
    import Box from "@mui/material/Box";
    import Toolbar from "@mui/material/Toolbar";
    import IconButton from "@mui/material/IconButton";
    import Typography from "@mui/material/Typography";
    import Menu from "@mui/material/Menu";
    import MenuIcon from "@mui/icons-material/Menu";
    import Container from "@mui/material/Container";
    import Avatar from "@mui/material/Avatar";
    import Button from "@mui/material/Button";
    import Tooltip from "@mui/material/Tooltip";
    import MenuItem from "@mui/material/MenuItem";
    import AdbIcon from "@mui/icons-material/Adb";
    import Badge from "@mui/material/Badge";
    import NotificationsIcon from "@mui/icons-material/Notifications";
    import { Link, Navigate } from "react-router-dom";
    import { useContext } from 'react';
    import { useAuth } from '../utils/UserContext'; 
    
    function LoggedIn({children}) {
      const style = {
        textDecoration: "none",
        color: "white",
      };
      
      const { isLoggedIn, authUser } = useAuth();
    
      const home = <Link to={`/home`} style={style}>Home</Link>
      const products = <Link to={`/products`} style={style}>Products</Link>
      const pricing = <Link to={`/pricing`} style={style}>Pricing</Link>
      const blog = <Link to={`/blog`} style={style}>Blog</Link>
      const about = <Link to={`/about`} style={style}>About</Link>
      const profile = <Link to={`/profile`} style={style}>Profile</Link>
      const account = <Link to={`/account`} style={style}>Account</Link>
      const dashboard = <Link to={`/dashboard`} style={style}>Dashboard</Link>
      const logout = <Link to={`/logout`} style={style}>Logout</Link>
      
      const pages = [home, products, pricing, blog, about];
      const settings = [home, profile, account, dashboard, logout];
    
      const [anchorElNav, setAnchorElNav] = React.useState(null);
      const [anchorElUser, setAnchorElUser] = React.useState(null);
    
      const handleOpenNavMenu = (event) => {
        setAnchorElNav(event.currentTarget);
      };
      const handleOpenUserMenu = (event) => {
        setAnchorElUser(event.currentTarget);
      };
    
      const handleCloseNavMenu = () => {
        setAnchorElNav(null);
      };
    
      const handleCloseUserMenu = () => {
        setAnchorElUser(null);
      };
    
      const [userData, setUserData] = useState('');
    
        const getUserData = async () => {
            const res = await fetch(`http://localhost:8000/api/users/${authUser}`);
            const data = await res.json();
            const user_data = data.user; 
            
            console.log("DATA:", user_data)
    
            setUserData(user_data)
        };
    
        useEffect(() => {
          getUserData();
        }, []);
    
      return isLoggedIn ? (
      <>
        <AppBar position="static">
          <Container maxWidth="xl">
            <Toolbar disableGutters>
              <AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
              <Typography
                variant="h6"
                noWrap
                component="a"
                href="/blog"
                sx={{
                  mr: 2,
                  display: { xs: "none", md: "flex" },
                  fontFamily: "monospace",
                  fontWeight: 700,
                  letterSpacing: ".3rem",
                  color: "inherit",
                  textDecoration: "none",
                }}
              >
                {userData.user_name}
              </Typography>
    
              <Box sx={{ flexGrow: 1, display: { xs: "flex", md: "none" } }}>
                <IconButton
                  size="large"
                  aria-label="account of current user"
                  aria-controls="menu-appbar"
                  aria-haspopup="true"
                  onClick={handleOpenNavMenu}
                  color="inherit"
                >
                  <MenuIcon />
                </IconButton>
                <Menu
                  id="menu-appbar"
                  anchorEl={anchorElNav}
                  anchorOrigin={{
                    vertical: "bottom",
                    horizontal: "left",
                  }}
                  keepMounted
                  transformOrigin={{
                    vertical: "top",
                    horizontal: "left",
                  }}
                  open={Boolean(anchorElNav)}
                  onClose={handleCloseNavMenu}
                  sx={{
                    display: { xs: "block", md: "none" },
                  }}
                >
                  {pages.map((page) => (
                    <MenuItem key={page} onClick={handleCloseNavMenu}>
                      <Typography textAlign="center">{page}</Typography>
                    </MenuItem>
                  ))}
                </Menu>
              </Box>
              <AdbIcon sx={{ display: { xs: "flex", md: "none" }, mr: 1 }} />
              <Typography
                variant="h5"
                noWrap
                component="a"
                href=""
                sx={{
                  mr: 2,
                  display: { xs: "flex", md: "none" },
                  flexGrow: 1,
                  fontFamily: "monospace",
                  fontWeight: 700,
                  letterSpacing: ".3rem",
                  color: "inherit",
                  textDecoration: "none",
                }}
              >
              </Typography>
              <Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
                {pages.map((page) => (
                  <Button
                    key={page}
                    onClick={handleCloseNavMenu}
                    sx={{ my: 2, color: "white", display: "block" }}
                  >
                    {page}
                  </Button>
                ))}
              </Box>
    
              <Box sx={{ flexGrow: 0 }}>
                <Tooltip title="Open settings">
                  <IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
                    <Avatar
                      alt={userData.user_name}
                      src={userData.profile_pic}
                    />
                  </IconButton>
                </Tooltip>
                <Menu
                  sx={{ mt: "45px" }}
                  id="menu-appbar"
                  anchorEl={anchorElUser}
                  anchorOrigin={{
                    vertical: "top",
                    horizontal: "right",
                  }}
                  keepMounted
                  transformOrigin={{
                    vertical: "top",
                    horizontal: "right",
                  }}
                  open={Boolean(anchorElUser)}
                  onClose={handleCloseUserMenu}
                >
                  {settings.map((setting) => (
                    <MenuItem key={setting} onClick={handleCloseUserMenu}>
                      <Typography textAlign="center">{setting}</Typography>
                    </MenuItem>
                  ))}
                </Menu>
              </Box>
            </Toolbar>
          </Container>
        </AppBar>
        {children}
        </>
      ) : (
        <Navigate to="/" replace />
    );;
    }
    export default LoggedIn;
    
    

    App.js

    
    // import logo from './logo.svg';
    import './App.css';
    import { BrowserRouter, Routes, Route } from 'react-router-dom';
    import LoggedIn from './NavBar/LoggedIn';
    import Home from './Component/Home';
    import { About } from './Component/About';
    import { Blog } from './Component/Blog';
    import { Products } from './Component/Products';
    import { Pricing } from './Component/Pricing';
    import { UserContext } from './utils/UserContext';
    import { LoginPage } from './Component/LoginPage';
    import { AuthProvider, useAuth } from './utils/UserContext'
    
    function App() {
    
      return (
        <>
          <BrowserRouter>
            <AuthProvider>
              {/* <PrivateRoute>
                <LoggedIn />
              </PrivateRoute> */}
              <Routes>
                <Route path='/' element={<LoginPage />} />
                <Route path='/home' element={<LoggedIn><Home /></LoggedIn>} />
                <Route path='/about' element={<About />} />
                <Route path='/blog' element={<Blog />} />
                <Route path='/products' element={<Products />} />
                <Route path='/pricing' element={<Pricing />} />
              </Routes>
            </AuthProvider>
          </BrowserRouter>
        </>
        // <>
        //   <BrowserRouter>
        //     <AuthProvider>
        //       <Routes>
        //         <Route path='/' element={<LoginPage />} />
        //         <Route path='/home' element={<PrivateRoute> <Home authUser={authUser} /> </PrivateRoute>} />
        //         <Route path='/about' element={<About />} />
        //         <Route path='/blog' element={<Blog />} />
        //         <Route path='/products' element={<Products />} />
        //         <Route path='/pricing' element={<Pricing />} />
        //       </Routes>
        //     </AuthProvider>
        //   </BrowserRouter>
        // </>
      );
    }
    
    export default App;
    
    

    NOTE: notice the <Route path='/home' element={<LoggedIn><Home /></LoggedIn>} /> in App.js. I will do this for the other route paths. For the sake of my ego, I hope i was not the only one who did not know that you could wrap a component around another component in this use case but it works! Notice I passed in children as a prop to LoggedIn and rendered it after the rest of my code. You might be wondering why I just didnt render the LoggedIn component on top of all my pages BUT we're not gonna talk about that. Learned a lot in the process.


  2. The issue seems to be with the usage of the PrivateRoute component. The PrivateRoute component is wrapping the LoggedIn component, but it should be wrapping the components that need authentication. Additionally, the PrivateRoute component is not correctly checking if the user is authenticated before rendering the component.

    Here’s a possible solution:

    App.js

    import "./App.css";
    import { BrowserRouter, Routes, Route } from "react-router-dom";
    import LoggedIn from "./NavBar/LoggedIn";
    import Home from "./Component/Home";
    import { About } from "./Component/About";
    import { Blog } from "./Component/Blog";
    import { Products } from "./Component/Products";
    import { Pricing } from "./Component/Pricing";
    import { UserContext } from "./utils/UserContext";
    import { LoginPage } from "./Component/LoginPage";
    import { AuthProvider, useAuth } from "./utils/UserContext";
    import PrivateRoute from "./Component/PrivateRoute";
    
    function App() {
      const { isLoggedIn } = useAuth();
    
      return (
        <>
          <BrowserRouter>
            <AuthProvider>
              <Routes>
                <Route path="/" element={<LoginPage />} />
                <PrivateRoute path="/home">
                  <LoggedIn />
                  <Home />
                </PrivateRoute>
                <PrivateRoute path="/about">
                  <LoggedIn />
                  <About />
                </PrivateRoute>
                <PrivateRoute path="/blog">
                  <LoggedIn />
                  <Blog />
                </PrivateRoute>
                <PrivateRoute path="/products">
                  <LoggedIn />
                  <Products />
                </PrivateRoute>
                <PrivateRoute path="/pricing">
                  <LoggedIn />
                  <Pricing />
                </PrivateRoute>
              </Routes>
            </AuthProvider>
          </BrowserRouter>
        </>
      );
    }
    
    export default App;
    

    As you can see, we moved the PrivateRoute component inside the Routes component and we added the LoggedIn component to each of the protected routes.

    PrivateRoute.js

    import { Route, Navigate } from "react-router-dom";
    import { useAuth } from "../utils/UserContext";
    
    function PrivateRoute({ children, ...rest }) {
      const { isLoggedIn } = useAuth();
    
      return isLoggedIn ? (
        <Route {...rest}>{children}</Route>
      ) : (
        <Navigate to="/" replace />
      );
    }
    
    export default PrivateRoute;
    

    Also, we updated the PrivateRoute component to check if the user is authenticated before rendering the children components. If the user is not authenticated, it will redirect to the login page.

    With these changes, the useContext should be persisted throughout all components.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search