I have a react context variable called “isLoggedIn” in a file called ‘AuthContext.js’, which I wrap my App.js with. This isLoggedIn variable tracks if a user is logged in to dynamically render a navigation bar and various pages. Here is my AuthContext.js file and my App.js which is wrapped with AuthProvider:
import { createContext, useContext, useState, useEffect } from 'react';
import axios from 'axios';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
// You can use useEffect to check the user's session
useEffect(() => {
// Send a request to the /update1
//In summary, sending an empty request body with an empty Create1Dto object to
// your /update1 endpoint should not cause any changes to the existing data in the user's profile.
axios.get('http://localhost:8080/auth/isloggedin', {}, { withCredentials: true })
.then(response => {
if (response.status === 200) {
// Profile updated successfully, user is logged in
setIsLoggedIn(true);
} else {
// Handle other response statuses
setIsLoggedIn(false);
}
})
.catch(error => {
if (error.response && error.response.status === 400 && error.response.data === 'User not found') {
// User is not logged in or not authorized
setIsLoggedIn(false);
} else {
// Handle other errors
setIsLoggedIn(false);
}
});
}, [isLoggedIn]);
return (
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
return useContext(AuthContext);
};
and I wrapped my app with AuthProvider:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
import MyProfile from './MyProfile';
import { Box } from '@mui/material';
import ProfileCreation from './ProfileCreation';
import styled from '@emotion/styled';
import MainPage from './MainPage';
import QuizPage from './QuizPage';
import '../App.css'
import { useLocation } from 'react-router-dom';
import { AnimatePresence} from 'framer-motion';
import ResultsQuiz from './ResultsQuiz';
import LoginPage from './LoginPage';
import {useSelector} from 'react-redux';
import Create1 from './Create1';
import Create2 from './Create2';
import Create3 from './Create3';
import { AuthProvider } from '../components/AuthContext';
import { ProfileProvider } from '../components/ProfileContext';
import { ImageUploadProvider } from '../components/ImageUploadContext';
import LogoutPage from './LogoutPage';
import MyPersonalProfile from './MyPersonalProfile';
function AppContent() {
const location = useLocation();
const { isAuthenticated, user } = useSelector(state => state.auth);
//Use the isAuthenticated state to conditionally render components based on the user's authentication status.
const MainContainer = styled(Box)({
minWidth: '80rem', // or your desired value
minHeight: '45rem', // or your desired value
overflow: 'auto'
});
const questions = [
{ id: 1, text: "I am the life of the party." },
{ id: 2, text: "I feel little concern for others." },
{ id: 3, text: "I am always prepared." },
{ id: 4, text: "I get stressed out easily." },
{ id: 5, text: "I have a rich vocabulary." },
{ id: 6, text: "I don't talk a lot." },
{ id: 7, text: "I am interested in people." },
{ id: 8, text: "I leave my belongings around." },
{ id: 9, text: "I am relaxed most of the time." },
{ id: 10, text: "I have difficulty understanding abstract ideas." },
{ id: 11, text: "I feel comfortable around people." },
{ id: 12, text: "I insult people." },
{ id: 13, text: "I pay attention to details." },
{ id: 14, text: "I worry about things." },
{ id: 15, text: "I have a vivid imagination." },
{ id: 16, text: "I keep in the background." },
{ id: 17, text: "I sympathize with others' feelings." },
{ id: 18, text: "I make a mess of things." },
{ id: 19, text: "I seldom feel blue." },
{ id: 20, text: "I am not interested in abstract ideas." },
{ id: 21, text: "I start conversations." },
{ id: 22, text: "I am not interested in other people's problems." },
{ id: 23, text: "I get chores done right away." },
{ id: 24, text: "I am easily disturbed." },
{ id: 25, text: "I have excellent ideas." },
{ id: 26, text: "I have little to say." },
{ id: 27, text: "I have a soft heart." },
{ id: 28, text: "I often forget to put things back in their proper place." },
{ id: 29, text: "I get upset easily." },
{ id: 30, text: "I do not have a good imagination." },
{ id: 31, text: "I talk to a lot of different people at parties." },
{ id: 32, text: "I am not really interested in others." },
{ id: 33, text: "I like order." },
{ id: 34, text: "I change my mood a lot." },
{ id: 35, text: "I am quick to understand things." },
{ id: 36, text: "I don't like to draw attention to myself." },
{ id: 37, text: "I take time out for others." },
{ id: 38, text: "I shirk my duties." },
{ id: 39, text: "I have frequent mood swings." },
{ id: 40, text: "I use difficult words." },
{ id: 41, text: "I don't mind being the center of attention." },
{ id: 42, text: "I feel others' emotions." },
{ id: 43, text: "I follow a schedule." },
{ id: 44, text: "I get irritated easily." },
{ id: 45, text: "I spend time reflecting on things." },
{ id: 46, text: "I am quiet around strangers." },
{ id: 47, text: "I make people feel at ease." },
{ id: 48, text: "I am exacting in my work." },
{ id: 49, text: "I often feel blue." },
{ id: 50, text: "I am full of ideas." }
];
return (
<AnimatePresence>
<MainContainer>
<AuthProvider>
<ProfileProvider>
<Routes location={location} key={location.pathname}>
<Route path="/myprofile" element={<MyPersonalProfile />} />
<Route path="/" element={<MainPage />} />
<Route path="/create" element={<ProfileCreation />} />
<Route path = "/create/1" element ={<Create1 />} />
<Route path = "/create/2" element ={<Create2 />} />
<Route path = "/create/3" element ={<Create3 />} />
<Route path="/quiz/:questionId" element={<QuizPage questions={questions}/> } />
<Route path="/resultsquiz" element={<ResultsQuiz />}/>
<Route path="/login" element={<LoginPage />} />
<Route path="/logout" element={<LogoutPage />} />
</Routes>
</ProfileProvider>
</AuthProvider>
</MainContainer>
</AnimatePresence>
);
}
export default AppContent;
In my LoginPage, which is a sibling component of MainPage in the react router, I update isLoggedIn to ‘true’ after successful login and then use ‘Navigate’ to navigate back to the main page. But when I am on the main page, when I console.log “isLoggedIn”, it shows up as false and the conditional rendering for the navbar is incorrect. I don’t understand why my isLoggedIn state from my context is not updating. Here is my code for LoginPage.js and my code for MainPage.js.
import * as React from 'react';
import Box from '@mui/material/Box';
import styled from '@emotion/styled';
import RightBox from '../components/RightBox.js';
import CreateNavbar from '../components/CreateNavbar.js';
import LeftBox from '../components/LeftBox.js'
import { Typography } from "@mui/material";
import InputField from '../components/InputFIeld.js';
import {motion} from 'framer-motion';
import { useTheme } from '@emotion/react';
import {Button} from '@mui/material';
import { useState } from 'react';
import SelectError from '../components/SelectError.js';
import { useEffect } from 'react';
import jwt_decode from 'jwt-decode';
import { login, logout } from '../redux/authSlice.js'
import { useSelector, useDispatch } from 'react-redux';
import Cookies from 'js-cookie';
import LoginForm from '../components/LoginForm.js';
import Stack from '@mui/material/Stack';
import LogButton from '../components/LogButton.js';
import axios from 'axios';
import { useAuth } from '../components/AuthContext.js';
import SelectSuccess from '../components/SelectSuccess.js';
import { Navigate, useNavigate } from 'react-router-dom';
import MainNavbar from '../components/MainNavbar.js';
import PasswordInputField from '../components/PasswordInputField.js';
import { useProfile } from '../components/ProfileContext.js';
const FullPageCenter = styled('div')({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100vh', // Set the minimum height to 100% of the viewport height
});
//this is to center the content
const ItalicText = styled('div')(({ theme }) => ({
fontFamily: theme.typography.h1.fontFamily,
fontStyle: 'italic',
fontSize: '8rem',
color: theme.palette.accent.main, // Accessing the accent.main color
// other styles for your component
lineHeight: '0.9',
}));
const Wrapper = styled('div')({
justifyContent: 'center',
alignItems: 'center',
height: '45rem',
width: '80rem',
overflowX: 'hidden',
overflowY: 'scroll',
marginBottom: '4rem',
position: 'relative', // This is important for the absolute positioning of the child.
});
//this is to prevent resizing....
//does putting position of top level copmonent as relative make all the child components, including grandchildren components, relative to the top level component?
//this is relative position only deals with direct children that are absolutely positioned
//wrapper is outermost container...centers stuff in scree horizontall yand vefitcally
//overflowing content will be clipped
//relative means child elements will be positioned relative to wrapper
const CenterContainer = styled(Box)(({ theme }) => ({
width: '98%',
display: 'flex', //this ensures marginLeft: auto on right box pushes to right
minHeight: '98%',
height: '98%',
margin: 'auto',
marginTop: '4rem',
overflowX: 'hidden',
overflowY: 'hidden',
//if there isnt enough space, parent container forces horizonal scroll
}));
//this is immediate child of wrapper, contains right box
//relative makes container point of ref to absolutely positioned children
//overflowX: 'scroll' and overflowY: 'scroll': These styles force both horizontal and vertical scrollbars to appear if the content inside CenterContainer exceeds its boundaries.
// you can add styling on top of navbar to position it!
export default function LoginPage({children}) {
const [ token, setToken ] = useState('');
const [ activeButton, setActiveButton ] = useState('LOGIN') //default to log in
const { isAuthenticated, user } = useSelector(state => state.auth);
//useSelector is reading variable from reducer
//we want to take these variables from redux state
const theme = useTheme();
const {isLoggedIn, setIsLoggedIn} = useAuth();
const {isProfileCreated} = useProfile();
const [ success, setSuccess ] = useState(false);
const [error, setError ] = useState(false);
const [taken, setTaken] = useState(false);
const navigate = useNavigate();
const handleLogin = async (inputUsername, inputPassword) => {
try {
const loginDto = {
username: inputUsername,
password: inputPassword,
// Include other registration data fields here
};
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
withCredentials: true,
};
const response = await axios.post('http://localhost:8080/auth/login', loginDto, config);
if (response.status === 200) {
// Successful login
setIsLoggedIn(true);
navigate('/');
console.log('Login successful!');
} else {
// Handle login failure
setIsLoggedIn(false);
setError(true);
console.error('Login failed.');
}
} catch (error) {
// Handle other errors
setIsLoggedIn(false);
setError(true);
console.error('Error during login:', error);
}
};
//if spring security session management exists in java backend, dont need to make front end handle sessions
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData(document.getElementById("submission")); // Use event.currentTarget
const inputUsername = formData.get("username");
const inputPassword = formData.get("password");
console.log("this is the formdata's username:" , inputUsername)
console.log("formdata", Object.fromEntries(formData));
if (activeButton === "LOGIN") {
handleLogin(inputUsername, inputPassword);
} else if (activeButton === "REGISTER") {
handleRegister(inputUsername, inputPassword);
}
};
const handleRegister = async (inputUsername, inputPassword) => {
if (isLoggedIn === true ){
console.log("Need to log out");
return;
}
// Logic for registration
try {
const registrationDto = {
username: inputUsername,
password: inputPassword,
// Include other registration data fields here
};
const config = {
headers: {
'Content-Type': 'application/json', // Set the request header to JSON
},
};
const response = await axios.post('http://localhost:8080/auth/register', registrationDto, config);
if (response.status === 200) {
// Successful registration
console.log('Registration successful!');
setSuccess(true);
} else {
// Handle registration failure
console.error('Registration failed.');
setTaken(true);
}
} catch (error) {
// Handle other errors
console.error('Error during registration:', error);
}
};
useEffect(() => {
console.log(isLoggedIn, "am i logged in");
console.log(isProfileCreated, "do i have a profile created")
}, []);
return (
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
exit={{opacity: 0, transition: {duration: 0.4}}}>
<FullPageCenter>
<Wrapper>
<MainNavbar />
{/* adding navbar above container so its rendered above containers... */}
<CenterContainer>
<LeftBox>
{ isLoggedIn===true &&
<div sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100v' }}>
<ItalicText style={{wordWrap:"break-word", overflowWrap: "break-word"}}>You are logged in!</ItalicText>
</div>
}
{
(isLoggedIn !== true) &&
<ItalicText style={{marginLeft: '6rem', wordWrap:"break-word", overflowWrap: "break-word", marginBottom: '5rem',marginTop: '0.7rem'}}>Log in to your account</ItalicText>
}
</LeftBox>
<RightBox justify="yes">
{
isLoggedIn!==true &&
<Stack spacing="1rem" >
<Box flexDirection="row" style={{ display: 'flex', width: '100%' }} justifyContent="space-between">
{/* the space between is inside the parent stack */}
<LogButton label="LOGIN" width='9rem' onClick={() => setActiveButton('LOGIN')} isActive={activeButton==='LOGIN'}/>
<LogButton label="REGISTER" width='9rem' onClick={()=> setActiveButton('REGISTER')} isActive={activeButton==='REGISTER'}/>
{/* i use isActive...boolean, if true then colors it blue */}
</Box>
{/* <Stack spacing="1rem"> don't need this stack */}
<form onSubmit={handleSubmit} id="submission">
<InputField name="username" label="USERNAME" />
<PasswordInputField name="password" label="PASSWORD" />
<LogButton onClick={handleSubmit} label="submit" color="#000000" style={{flex:1}}/>
</form>
{/*the submit button fills the space in the flex component..the stack component */}
{/* </Stack> */}
{/* <AuthContent /> */}
</Stack>
}
{ isLoggedIn===true &&
<p>Time to go find your traitmate!</p>
}
{error &&
<SelectError message="Wrong login credentials. Try again?"></SelectError>
}
{
success &&
<SelectSuccess message="Successfully registered! Now log in."></SelectSuccess>
}
{
taken &&
<SelectError message="The username is already taken! Try another"></SelectError>
}
</RightBox>
</CenterContainer>
</Wrapper>
</FullPageCenter>
</motion.div>
);
}
and MainPage.js:
import React from "react";
import MainHeader from "../components/MainHeader";
import Box from '@mui/material/Box';
import styled from '@emotion/styled';
import {motion} from 'framer-motion';
import MainNavbar from "../components/MainNavbar";
import { useAuth } from "../components/AuthContext";
import { useProfile } from "../components/ProfileContext";
import { useEffect } from "react";
const FullPageCenter = styled('div')({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100vh', // Set the minimum height to 100% of the viewport height
//position: 'relative', //ensure relative positioning for stacking context
backgroundImage: 'url("/MOONASSI_16_UNTITLED.png")', // <-- add this line
backgroundSize: '800px', // cover ensures the image fills the container without stretching
backgroundPosition: '17rem bottom', // center the image
backgroundRepeat: 'no-repeat', // don't repeat the image
position: 'relative',
});
const Wrapper = styled('div')({
justifyContent: 'center',
alignItems: 'center',
height: '45rem',
width: '100%',
overflowX: 'scroll',
overflowY: 'scroll',
marginBottom: '4rem',
position: 'relative',
// This is important for the absolute positioning of the child.
});
//this is to prevent resizing....
//does putting position of top level copmonent as relative make all the child components, including grandchildren components, relative to the top level component?
//this is relative position only deals with direct children that are absolutely positioned
//wrapper is outermost container...centers stuff in scree horizontall yand vefitcally
//overflowing content will be clipped
//relative means child elements will be positioned relative to wrapper
const CenterContainer = styled('div')(({ theme }) => ({
width: '100%',
display: 'flex', //this ensures marginLeft: auto on right box pushes to right
minHeight: '98%',
height: '98%',
marginTop: '2rem',
border: '1px solid black',
//if there isnt enough space, parent container forces horizonal scroll
}));
function MainPage() {
const {isLoggedIn} = useAuth();
const {isProfileCreated} = useProfile();
useEffect(() => {
console.log(isLoggedIn, "am i logged in");
console.log(isProfileCreated, "do i have a profile created")
}, []);
return (
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
exit={{opacity: 0, transition: {duration: 0.8}}}>
<FullPageCenter sx={{zIndex: -1000}}>
<Wrapper>
<CenterContainer>
<MainNavbar sx={{zIndex: 9999 }} />
<MainHeader/>
</CenterContainer>
</Wrapper>
</FullPageCenter>
</motion.div>
);
}
export default MainPage;
My navbar is also not receiving the isLoggedIn state update and is incorrectly rendering:
import React from "react";
import { AppBar, Toolbar, CssBaseline, Typography } from "@mui/material";
import { Link } from "react-router-dom";
import styled from "@emotion/styled";
import { useAuth } from "./AuthContext";
import { useProfile } from "./ProfileContext";
import { BrowserRouter as Router } from 'react-router-dom';
import { useEffect } from "react";
const NavbarWrapper = styled('div')({
width: '100vw',//adding this centers the navbar
height: '4rem', // Set the height to 4rem
// Add any other styling you need for the navbar
position: 'fixed',
overflow: 'visible',
zIndex: '1000',
pointerEvents: 'auto', //set this to high value to make text hoverable in MyProfile.js
// i think this overflow auto is what causes it to be fixed size
});
const NavbarAppBar = styled(AppBar)`
background-color: transparent;
`;
const NavbarToolbar = styled(Toolbar)`
display: flex;
justify-content: center;
align-items: center;
`;
const NavbarNavlinks = styled.div`
display: flex;
align-items: center;
flex: 1;
justify-content: space-between; /* Spread items evenly */
align-items: center;
margin: 10rem 10rem;
gap: 9rem;
pointer-events: 'auto';
`;
const NavbarLink = styled(Link)(({ theme }) => ({
fontFamily: theme.typography.body2.fontFamily,
fontWeight: 300,
pointerEvents: 'auto',
textDecoration: 'none',
zIndex: 9999,
color: 'black',
'&:hover': {
color: 'blue',
borderBottom: '1px solid blue'
}
}));
function MainNavbar() {
const {isLoggedIn} = useAuth();
const {isProfileCreated} =useProfile();
useEffect(() => {
console.log(isLoggedIn, "am i logged in from navbar?");
}
, []);
useEffect(() => {
console.log('MainNavbar re-rendered with isLoggedIn value:', isLoggedIn);
});
// in styled, for font weight, remove the brackets
return (
<NavbarWrapper>
<NavbarAppBar position="static" elevation={0} sx={{background: '#dfd3bc', maxHeight:'4rem',}}>
{/* the sx prop has a higher specificity...easier to override styles */}
<CssBaseline/>
<NavbarToolbar>
{ (isLoggedIn===false || isLoggedIn===null) &&
<NavbarNavlinks>
<NavbarLink to="/login"><b>LOG IN</b></NavbarLink>
<NavbarLink to="/"> <b>MAIN</b></NavbarLink>
<a href="http://jasminenoodlewavey.vercel.app" style={{ textDecoration: 'none', color: 'black'}}>ABOUT THE CREATOR</a>
</NavbarNavlinks>
}
{ isLoggedIn===true && isProfileCreated===false &&
<NavbarNavlinks>
<NavbarLink to="/logout"><b>LOG OUT</b></NavbarLink>
<NavbarLink to="/create/1"> <b>CREATE PROFILE</b></NavbarLink>
<a href="http://jasminenoodlewavey.vercel.app" style={{ textDecoration: 'none', color: 'black'}}>ABOUT THE CREATOR</a>
</NavbarNavlinks>
}
{ isLoggedIn===true && isProfileCreated===true &&
<NavbarNavlinks>
<NavbarLink to="/logout"><b>LOG OUT</b></NavbarLink>
<NavbarLink to="/myprofile"> <b>MY PROFILE</b></NavbarLink>
<NavbarLink to="/match"> <b>FIND YOUR MATCH</b></NavbarLink>
<a href="http://jasminenoodlewavey.vercel.app" style={{ textDecoration: 'none', color: 'black'}}>ABOUT THE CREATOR</a>
</NavbarNavlinks>
}
</NavbarToolbar>
</NavbarAppBar>
</NavbarWrapper>
);
}
export default MainNavbar;
Is this an issue with my use of Navigation? Does my redirect from LoginPage.js to MainPage.js reset the useContext value? I’m unsure about how to proceed since I think I’ve implemented my AuthContext correctly….