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
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:
<a href="/home" className="text-sm font-semibold leading-6 text-black-900">Home</a>
to this:
<Link to="/home" className="text-sm font-semibold leading-6 text-black-900">Home</Link>
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:
const updateUser = (userData: UserData | undefined) => {
if(userData == undefined) localStorage.removeItem("userData")
else localStorage.setItem("userData", JSON.stringify(userData))
setUser(userData);
};
;
and use useEffect
hook to get the data back from the local storage once the page reloads:
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [user, setUser] = useState<UserData | undefined>(undefined);
useEffect(() => {
const storedData = localStorage.getItem("userData");
if (storedData) {
const parsedData: UserData = JSON.parse(storedData);
setUser(parsedData);
}
}, [])
};
i hope this helps.