This is the AuthProvider file that I am using to wrap my whole application
// AuthContext.tsx
import React, { createContext, useContext, useState, ReactNode } from "react";
interface UserData {
username: string;
password: string;
id: string;
first_name: string;
last_name: string;
postal: string;
email: string;
telephone_number: string;
}
interface AuthContextProps {
user: UserData | undefined;
setUser: (userData: UserData | undefined) => void;
}
export const AuthContext = createContext<AuthContextProps | undefined>(undefined);
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [user, setUser] = useState<UserData | undefined>(undefined);
const updateUser = (userData: UserData | undefined) => {
setUser(userData);
};
return (
<AuthContext.Provider value={{ user, setUser: updateUser }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = (): AuthContextProps => {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};
this is part of the navbar that you will need to check
export default function Example() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
// const { user, setUser } = useAuth();
const context = useContext(AuthContext);
return (
<AuthProvider>
<header className="bg-gradient-to-r from-blue-50 via-blue-300 to-blue-500">
<nav
className="mx-auto flex max-w-7xl items-center justify-between p-0 lg:px-8"
aria-label="Global"
>
<div className="flex lg:flex-1">
<a href="#" className="-m-1.5 p-1.5">
<span className="sr-only">Your Company</span>
<img
className="h-8 w-auto"
src={imgLib}
alt=""
style={{ width: "170px", height: "170px" }}
/>
</a>
</div>
<div className="flex lg:hidden">
<button
type="button"
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700"
onClick={() => setMobileMenuOpen(true)}
>
<span className="sr-only">Open main menu</span>
<Bars3Icon
className="h-6 w-6 mr-5"
aria-hidden="true"
style={{ color: "white" }}
/>
</button>
</div>
<Popover.Group className="hidden lg:flex lg:gap-x-12">
<a
href="/home"
className="text-sm font-semibold leading-6 text-black-900"
>
Home
</a>
<a
href="/about"
className="text-sm font-semibold leading-6 text-black-900"
>
About us
</a>
<a
href="/contact"
className="text-sm font-semibold leading-6 text-black-900"
>
Contact
</a>
<a className="dropdown">
<a
className="dropbtn"
style={{ color: "black", fontWeight: "550" }}
>
Library
<i className="fa fa-caret-down"></i>
</a>
<div className="dropdown-content">
<a href="/library">Book store</a>
<a href="/meeting-room">Meeting room</a>
<a href="/book-table">My books</a>
</div>
</a>
</Popover.Group>
<div className="hidden lg:flex lg:flex-1 lg:justify-end">
{context?.user === undefined ? (
<a
href="/"
className="text-sm font-semibold leading-6 text-black-900"
>
Log in <span aria-hidden="true">→</span>
</a>
) : (
<a
onClick={() => context.setUser(undefined)}
href="/"
className="text-sm font-semibold leading-6 text-black-900"
>
Log out <span aria-hidden="true">→</span>
</a>
// access the username here
)}
this is my app being wrapped
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
import Login from "./pages/Login.tsx";
import Signup from "./pages/Signup.tsx";
import { Routes, Route } from "react-router-dom";
import Home from "./pages/Home.tsx";
import socket from "./Socket.tsx";
import { NotificationProvider } from "./context/NotificationProvider.tsx";
import Navbar from "./components/Navbar.tsx";
import { AuthProvider } from "./context/AuthProvider.tsx";
// import './styles/About.css'
// import './styles/Library.css'
// import './styles/Home.css'
// import './styles/myBooks.css'
//meeting-room
import About from "./pages/About.tsx";
import Library from "./pages/Library.tsx";
import BookTable from "./components/BookTable.tsx";
import MeetingRoomsPage from "./pages/BookingRoomsPage.tsx";
import Contact from "./pages/Contact.tsx";
import ReserveRooms from "./pages/ReservedRooms.tsx";
ReactDOM.createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<NotificationProvider />
<AuthProvider>
<Routes>
<Route path="/" element={<Login></Login>}></Route>
<Route path="/signup" element={<Signup></Signup>}></Route>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/about" element={<About></About>}></Route>
<Route path="/library" element={<Library></Library>}></Route>
<Route path="/book-table" element={<BookTable></BookTable>}></Route>
<Route path="/meeting-room" element={<MeetingRoomsPage></MeetingRoomsPage>}></Route>
<Route path="/reserver-rooms" element={<ReserveRooms></ReserveRooms>}></Route>
<Route path="/contact" element={<Contact></Contact>}></Route>
</Routes>
</AuthProvider>
</BrowserRouter>
);
I just tried to console log in the other pages where the option of log in return and I found that the context becomes undefined when navigating to other pages. It should be defined with user info so that the option of log in never go away unless I log out
2
Answers
The problem is that your AuthProvider is wrapped around your routes, but the Navbar component that contains the logic to display the login/logout link is rendered outside of the AuthProvider. In your file, you have this structure:
Since the navigation bar is rendered outside of the AuthProvider, it cannot access the AuthContext, so the context becomes undefined.
You need to move the Navbar component inside the AuthProvider.
By rendering the Navbar component inside the AuthProvider, it will have access to the AuthContext and should properly display the login/logout link based on the user’s authentication status.
When navigating through
<a/>
element it causes the page to refresh, leading to the resetting of the context. a possible solution would be to use<Link/>
element from the"react-router-dom"
instead of the<a/>
to prevent the page from refreshing, like changing this:to this:
and the same for the others.
A better solution would be to store the data you don’t want to lose in browser cookies or local storage and load it once the website opens using
useEffect
.an Example using local storage, assuming you call
updateUser
whenever the use log in, change it to:and use
useEffect
hook to get the data back from the local storage once the page reloads:i hope this helps.