Enhance the Header component by incorporating a logout button that seamlessly navigates with the NextJS App

Currently, I am utilizing NextJS 14 with App router alongside Spring Boot on the backend. Within my application, I have both public and private routes set up. For the private routes, users are required to log in through a designated login page. Upon successful authentication, a JWT token is returned as a cookie.

The following code snippet represents my root layout:

import "./global.css";
import Header from "./components/header/header";
import {GlobalProvider} from "./_lib/global-provider"; 
import MainContent from "./components/main-content";

export default async function RootLayout({children}) {

    return (
            <html lang="en" className="">
            <body className="min-h-screen scroll-smooth">
            <div className="flex flex-col min-h-screen">
                <GlobalProvider>
                    <Header/>
                    <MainContent>
                        {children}
                    </MainContent>
                </GlobalProvider>
            </div>
            </body>
            <GoogleAnalytics/>
            </html>
    );
}

Additionally, here is the Header component responsible for rendering the Log Out button when a user is logged in:

"use client";

import Link from "next/link";
import {useAdminAuthContext} from "../../_lib/admin-auth-provider";
import LogoHeader from "./logo-header";

export default function Header() {

    const adminAuth = useAdminAuthContext();

    return (
            <header className="sticky top-0 z-50 flex-none bg-gray-50 py-6 drop-shadow">
                <nav className="relative mx-auto flex justify-between px-8 max-sm:px-2">
                    <div className="flex items-center ">
                        <LogoHeader/>
                    </div>

                    <div className="flex items-center">
                        {
                            adminAuth.user != null && <button>LOG OUT</button>
                        }
                    </div>
                </nav>
            </header>
    );
}

Utilizing an AuthContext enables me to expose functionalities like logIn and logOut, which interact with the backend to determine if a user is authenticated. However, upon reloading the page, the state is lost causing the LOG OUT button to disappear. Here is the relevant AuthContext code:

"use client";

import {createContext, useContext, useEffect, useState} from "react";
import {useRouter} from 'next/navigation';

const host = process.env.NEXT_PUBLIC_API_HOST;
export const AdminAuthContext = createContext({});
export const AdminAuthProvider = ({children}) => {
    const router = useRouter(); // Initialize useRouter

    const [user, setUser] = useState(null); // State to store admin user details

    const doAdminLogin = async (email, password) => {
        const request = await fetch(`${host}/api/admin/login`, {
            cache: "no-store",
            method: "POST",
            credentials: "include",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({email, password}),
        });

        if (!request.ok) {
            console.error('Login failed');
            throw new Error("Error logging in");
        }

        const res = await request.json();
        setUser(res.username);
        router.push('/private/admin/dashboard'); // Use router.push to navigate
    };

    const doAdminLogout = async () => {
        const request = await fetch(`${host}/api/admin/logout`, {
            cache: "no-store",
            method: "POST",
            credentials: "include",
        });
    };

    return (
            <AdminAuthContext.Provider value={{user, doAdminLogin, doAdminLogout}}>
                {children}
            </AdminAuthContext.Provider>
    );
};

export function useAdminAuthContext() {
    return useContext(AdminAuthContext);
}

Therefore, the key question at hand is: What adjustments should be made to ensure that the Header component displays the LOG OUT button appropriately based on the user's authentication status?

Answer №1

Whenever you reload the page, all state information is lost. To retain data from a previous session, it's necessary to utilize browser storage mechanisms such as cookies or localStorage.

In this scenario, because a cookie is set upon login, you can easily trigger an API call on page load to verify the cookie and retrieve user information like this:

"use client";

import { createContext, useContext, useEffect, useState } from "react";
import { useRouter } from "next/navigation";

const host = process.env.NEXT_PUBLIC_API_HOST;
export const AdminAuthContext = createContext({});
export const AdminAuthProvider = ({ children }) => {
  const router = useRouter(); // Initialize useRouter

  const [user, setUser] = useState(null); // State to store admin user details
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    /* 
      Make use of the appropriate API endpoint to fetch the user data. If a cookie exists, 
      obtained from a prior login, you should receive the user information
      just like during the initial login.
   */
    const checkAdminLogin = async () => {
      try {
        const request = await fetch(`${host}/api/admin/user`, {
          method: "GET",
          credentials: "include",
        });

        if (request.ok) {
          const res = await request.json();
          setUser(res.username);
        } else {
          throw new Error("Not authorized");
        }
      } catch (error) {
        console.error(error);
        router.push("/admin/login");
      } finally {
        setLoading(false);
      }
    };
    checkAdminLogin();
  }, [router]);

  const doAdminLogin = async (email, password) => {
    // ...
  };

  const doAdminLogout = async () => {
    // ...
  };

  return (
    <AdminAuthContext.Provider value={{ user, doAdminLogin, doAdminLogout }}>
      {loading ? "Loading..." : children}
    </AdminAuthContext.Provider>
  );
};

export function useAdminAuthContext() {
  return useContext(AdminAuthContext);
}

Answer №2

When navigating pages, component level state is always lost. To prevent this issue, Redux internally connects components to the window object. I suggest utilizing Redux, possibly with a persistent storage plugin for extended storage persistence. With Redux alone, you should not encounter the current problem of losing state during page navigation.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring Angular.js: Finding the correct path in a JSON array

Within my Angular application, I have a $scope variable defined as follows. $scope.roleList2 = [ { "roleName" : "User", "roleId" : "role1", "children" : [ { "roleName" : "subUser1", "roleId" : "role11", "collapsed" : true, "children" : [ ...

What could be the reason why the initial console.log is failing to print?

Apologies for the oversight. The !== was a mistake that slipped past me before posting. Thank you for your understanding. I am a beginner in Javascript. I have written this function with winston: function setlogger(log_level = "warn", logfile, scree ...

Vue - Error Message from Eslint Regarding Absence of Variable in Setup Function

Interestingly, the Vue.js documentation strongly recommends using the <script setup> syntax of the Composition API. The issue with this recommendation is that the documentation lacks depth and it conflicts with other tools (like eslint). Here is an e ...

Tips for Successfully Sending Vue Data in Axios POST Call

I'm struggling to pass Vue data to an axios.post request. Using the Vue template doesn't seem to work. How can I successfully pass the Data? Here is my Code: <body> <div id="app" class="container"> <div> ...

Receiving JSON using Javascript and vue.js

When attempting to fetch json data in my vue.js application, I use the following code: new Vue({ el: 'body', data:{ role: '', company: '', list:[], ...

In JavaScript, JSON data is parsed using a comma separator

Here is the format of my data: [{"QualID":1,"Qualification":"Van Driver"},{"QualID":3,"Qualification":"Safety Cert"},{"QualID":4,"Qualification":"Welder"}] I am look ...

Front-end procedural logic for increasing identification values

$scope.items.push({ "itemId": $scope.tabId + 1, "itemName" : itemName, }); Whenever I try to push the item, I always console.log($scope.itemId) but it remains the same without increasing. One way to handle this issue could be utilizing $http after each ...

Guide on leveraging Next-auth for managing multiple sessions concurrently

I am currently developing an integration tool similar to Zappier. My goal is to utilize Next-auth for connecting to multiple applications and saving their access tokens. However, I have encountered a limitation with Next-auth only allowing for one sessio ...

Displaying a div on click using Jquery will only impact one div at a time

I am encountering an issue with my WordPress setup. Within my loop, I have a clickable link that toggles a div to show or hide content. However, since each item in the loop uses the same class, clicking on one link activates all of them simultaneously. &l ...

Error: The next.config.js file contains invalid options - The root value includes an unexpected property

I recently updated my next version from 10 to 12, and when I run the local development server, I encounter the following error in the terminal. As a result, the code fails to compile. How can I fix this issue? Invalid next.config.js options have been iden ...

Go back to the top by clicking on the image

Can you help me with a quick query? Is it feasible to automatically scroll back to the top after clicking on an image that serves as a reference to jQuery content? For instance, if I select an image in the "Portfolio" section of , I would like to be tak ...

An unexpected type error occurred: Unable to read the undefined property 'map' when utilizing Highcharts

I am currently working on developing highcharts using data from Firebase. I came across a helpful example here: However, when I try to integrate it into my application, I encounter the following error: firebase.js:43 Uncaught TypeError: Cannot read pr ...

JavaScript effectively divides multiple child dropdowns with the main dropdown, thanks to Bootstrap innovation

I have implemented a jQuery function to dynamically change multiple child dropdowns based on the selected value of the main dropdown. For reference, you can view the image here. However, I encountered an issue when applying the Bootstrap styles "form-con ...

The size of the array within the object does not align

I've run into a roadblock while attempting to implement the tree hierarchy in D3. Initially, I believed that I had correctly structured the JSON data, but upon inspecting the object using Developer's Tool, a discrepancy caught my eye: https://i. ...

What is the most efficient way to transfer form data from one JSP page to another?

I need to transfer information from one webpage (1.jsp) to another webpage (2.jsp). The data that needs to be transferred includes checkboxes, radio buttons, and drop downs. This data will be used in 2.jsp to generate the appropriate page. Is there a way ...

Incorporating the power of ES6 into a pre-existing website using React

I currently have an established website with a page accessible through the URL www.example.com/apps/myApp. The myApp functionality is embedded within an existing HTML page and serves as a utility app. I am interested in learning React, so I see this as a g ...

Ways to address the problem of returning assignments

I encountered an issue while working on my code Error message: Arrow function should not return assignment no-return-assign The problematic code snippet is as follows: await DB.Place.find( match, // key to filter (err, doc) => (listOfObje ...

Switch to a different element by rolling over one

I need assistance with a menu setup where the submenus are located in separate divs on the page. Both the parent elements and submenus have numerical identifying IDs, and I am struggling to create a rollover effect that would automatically open the corresp ...

What are the steps to fix a timeout error with React.js and socket.io acknowledgements?

My setup includes a Node.js server and a React.js client application. Data is exchanged between them using socket.io, but I'm running into an issue with implementing acknowledgment. Whenever I try to implement acknowledgment, I receive a timeout error ...

jQuery AJAX event handlers failing to trigger

It's driving me crazy! I've been using jquery's ajax for years, and I can't seem to figure out why the success, error, and complete events won't fire. The syntax is correct, the service it's calling works fine, but nothing hap ...