skip to Main Content

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">&rarr;</span>
              </a>
            ) : (
              <a
                onClick={() => context.setUser(undefined)}
                href="/"
                className="text-sm font-semibold leading-6 text-black-900"
              >
                Log out <span aria-hidden="true">&rarr;</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


  1. 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:

    ReactDOM.createRoot(document.getElementById("root")!).render(
        <BrowserRouter>
            <NotificationProvider />
            <AuthProvider> {/* AuthProvider wraps around Routes */}
                <Routes>
                    {/* Routes go here */}
                </Routes>
            </AuthProvider>
        </BrowserRouter>
    );
    

    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.

    ReactDOM.createRoot(document.getElementById("root")!).render(
        <BrowserRouter>
            <NotificationProvider />
            <AuthProvider> {/* AuthProvider wraps around everything */}
                <Navbar /> {/* Render Navbar inside AuthProvider */}
                <Routes>
                    {/* Routes go here */}
                </Routes>
            </AuthProvider>
        </BrowserRouter>
    );
    

    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.

    Login or Signup to reply.
  2. 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.

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