0

I have been working on this authentication for weeks now, and finally throwing in the towel as it has become a beast that is untamable. From my code below, I want the Sign Out and Sign In | Register links to change state if user is not authenticated. In other words, if isAuthenticated && userId is false I want the Sign In | Register link to to show, but if isAuthenticated && userId is true I want the Sign Out link to show without having to refresh the page.

The issue is that they change state only after I refresh the page. Below is a copy of the code:

import { useState, useEffect } from "react";
import Link from "next/link";
import { Client, Account } from 'appwrite';
import { useAuth } from "../pages/AuthContext";
import { useRouter } from 'next/navigation';

export default function Navbar() {
  const { logOut, logIn } = useAuth();
  const [user, setUser] = useState(null);
  const [errors, setErrors] = useState('');
  const [userId, setUserId] = useState("undefined");
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [showSignOutLink, setShowSignOutLink] = useState(false);
  const router = useRouter();

  const client = new Client();
  const account = new Account(client);

  client
    .setEndpoint(process.env.NEXT_PUBLIC_ENDPOINT)
    .setProject(process.env.NEXT_PUBLIC_PROJECT_ID);

  useEffect(() => {
    const checkSession = async () => {
      try {
        const response = await account.getSession("current");
        if (response.current && response.userId) {
          setUserId(response.userId);
          setIsAuthenticated(true);
        } else {
          setIsAuthenticated(false);
        }
      } catch (error) {
        setIsAuthenticated(false);
      }
    };

    const fetchUserData = async () => {
      try {
        const response = await account.get();
        if (response) {
          setUser(response.name);
        }
      } catch (error) {
        setErrors(error.message);
      }
    };

    checkSession();

    if (isAuthenticated) {
      fetchUserData();
    }
  }, [isAuthenticated]);

  const handleLogout = async () => {
    try {
      await account.deleteSession('current');
      logOut();
      router.push('/');
    } catch (error) {
      console.error("Logout Error:", error);
    }
  };

  useEffect(() => {
    setShowSignOutLink(isAuthenticated && userId !== null);
  }, [isAuthenticated, userId]);

return (
<> 
   <Menu>
        {showSignOutLink ? (
         <button onClick={handleLogout}>Sign Out</button>
        ) : (
         <Link href="/login">Sign In | Register </Link>
        )}
   </Menu>
</>
 )
}
Xhris
  • 55
  • 7

2 Answers2

0

Try changing the useEffect like this

useEffect(() => {
     if (isAuthenticated && userId !== null) {
         setShowSignOutLink(true);
     } else setShowSignOutLink(false);
  }, [isAuthenticated, userId]);
Lokkesh
  • 92
  • 8
  • Thanks for the response @Lokkesh. After making the changes the issue still persists. I still had to refresh the page for the links to switch. – Xhris Aug 26 '23 at 11:39
  • Pls create a sandbox @Xhris so it would be easy to identify the issue – Lokkesh Aug 26 '23 at 11:42
  • Thanks again @Lokkesh. Give me a few mins to set it up. I'll circle back in a few. Thanks for your response. – Xhris Aug 26 '23 at 11:46
0

The effect dealing with the "setIsAuthenticated" is executed 1.) if the Navbar component is rendered the first time (mounted) 2.) if "isAuthenticated" state changes (based on dependencies of the effect)

Since this state "isAuthenticated" is only changed within this effect, this effect will not run anymore (without "refresh the page" or the "key" of the Navbar component changes, if there is one used).

It seems to be a solution to call "setIsAuthenticated(false)" within the handleLogout callback.

jack-leh
  • 36
  • 3
  • Thanks @jack-leh for the helpful suggestion. I am able to change the state of the `Log Out` button on click to logout... Nice! However, login in - I still have to refresh the page for the button to change state to `Log Out`. In other words, after login in, the button still says `Sign In` even though user is already signed in at that point. Any suggestion? – Xhris Aug 26 '23 at 12:12
  • Since there is no "login" event triggered within the component and based what I can see in the code you posted, I can see two options: 1) usage of "key" property of the Navbar (if a key changes, component will pre-render, but this code change outside of the component) 2) There is a AuthContext used, there might be a hook to get the status? – jack-leh Aug 26 '23 at 12:31
  • What would those two options be? – Xhris Aug 26 '23 at 12:34
  • In other words: there is some "global" state your component needs to be aware of. To get global state into components (beside the props) hooks are used (if return value of hooks change, component will re-rerender) ... I do not see any other ("good") option so far, sorry. – jack-leh Aug 26 '23 at 12:43
  • Yes, there is an `AuthContext` with this code. Basically it just sets the authenticated state to true or false based on their auth status. – Xhris Aug 26 '23 at 12:47
  • Sorry, character limit won't let me paste the code on here. – Xhris Aug 26 '23 at 12:49