Next.js Discord

Discord Forum

Server action isn't refreshing page content

Answered
Spectacled Caiman posted this in #help-forum
Open in Discord
Spectacled CaimanOP
When I click logout, the page isn't refreshing or acknowledging the change of the session or status
// handleSignOut
'use server'
import { signOut } from "../../server/auth";

export async function handleSignOut() {
    await signOut();
}

// Logout button
<MenuItem>
  <form action={handleSignOut}>
    <button type="submit" className="text-sm">
      <FontAwesomeIcon icon={faSignOutAlt} className="mr-2" /> Logout
    </button>
  </form>
</MenuItem>

// Navbar
export function Navigation(props: NavigationProps) {
  const { data: session, status } = useSession();

  useEffect(() => {
    console.log(status, session); // Doesn't log on signout
  }, [status, session])

  return (
    <>
      <h1>{session ? 'Logged In' : 'Not Logged in'}</h1>
      <DropdownMenu session={session} />
    </>
  )
Answered by Velvet ant
can you try with the signOut client side ?

import { signOut } from "next-auth/react"

// ...
<MenuItem>
  <button onClick={() => signOut()}>
     <FontAwesomeIcon icon={faSignOutAlt} className="mr-2" /> Logout
  </button>
</MenuItem>
View full answer

51 Replies

Velvet ant
is that useSession from next auth ?
@Velvet ant is that `useSession` from next auth ?
Spectacled CaimanOP
import { useSession } from 'next-auth/react';
Velvet ant
did you set the provider ?
Spectacled CaimanOP
// layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={GeistSans.className}>
        <NextAuthProvider>
          <main>
            <div className="flex min-h-screen flex-col">{children}</div>
          </main>
        </NextAuthProvider>
      </body>
    </html>
  );
}

// NextAuthProvider.tsx
import { SessionProvider } from "next-auth/react";
import { ReactNode } from "react";

export default function NextAuthProvider({
  children,
}: {
  children: ReactNode;
}) {
  return <SessionProvider>{children}</SessionProvider>;
}
Velvet ant
NextAuthProvider has the "use client" directive ?
@Velvet ant `NextAuthProvider` has the "use client" directive ?
Spectacled CaimanOP
yes, my fault for omitting that part
Velvet ant
is it possible to see the api route ?
@Velvet ant is it possible to see the api route ?
Spectacled CaimanOP
// app/api/auth/[...nextauth]
import { handlers } from "@/server/auth";
export const { GET, POST } = handlers
Velvet ant
ok seems good to me, I do not use the client api but in next auth examples, it seems that they pass the session as prop to the provider https://github.com/nextauthjs/next-auth-example/blob/main/app/client-example/page.tsx
@Velvet ant ok seems good to me, I do not use the client api but in next auth examples, it seems that they pass the session as prop to the provider <https://github.com/nextauthjs/next-auth-example/blob/main/app/client-example/page.tsx>
Spectacled CaimanOP
this is what im doing now and it still doesnt seem to work
// layout.tsx
export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await auth();

  return (
    <html lang="en">
      <body className={GeistSans.className}>
        <NextAuthProvider session={session}>
          <main>
            <div className="flex min-h-screen flex-col">{children}</div>
          </main>
        </NextAuthProvider>
      </body>
    </html>
  );
}

// NextAuthProvider
"use client";

import { Session } from "next-auth";
import { SessionProvider } from "next-auth/react";
import { ReactNode } from "react";

export default function NextAuthProvider({
  children,
  session
}: {
  children: ReactNode;
  session: Session | null;
}) {
  return (
    <SessionProvider session={session}>
      {children}
    </SessionProvider>
  );
}
it seems like it's making the post request after refreshing or its taking longer than the refresh
Velvet ant
really trying to guess here, but maybe something with the base path https://next-auth.js.org/configuration/options#nextauth_url ? or idk maybe try to avoid the client side :/
@Velvet ant really trying to guess here, but maybe something with the base path <https://next-auth.js.org/configuration/options#nextauth_url> ? or idk maybe try to avoid the client side :/
Spectacled CaimanOP
all im trying to do is display the user name, avatar and the ability to logout which is why im on the client side
all works other than the fact that once i sign out, i have to refresh for it to remove the username and avatar
Velvet ant
I thin you can still display data and logout from server side.but yes sorry cant spot what wrong with client side :/
https://nextjs.org/learn/dashboard-app/adding-authentication#adding-the-logout-functionality
@Velvet ant I thin you can still display data and logout from server side.but yes sorry cant spot what wrong with client side :/ <https://nextjs.org/learn/dashboard-app/adding-authentication#adding-the-logout-functionality>
Spectacled CaimanOP
my signout is server side though
'use server'

import { signOut } from "../../server/auth";

export async function handleSignOut() {
    await signOut();
}
Velvet ant
I meant for the session you can get it server side with getServerSession
@Velvet ant I meant for the session you can get it server side with `getServerSession`
Spectacled CaimanOP
this is my first time using nextjs so this is all new to me. im not quite sure how to use getServerSession
@Velvet ant <https://next-auth.js.org/configuration/nextjs#in-app-router>
Spectacled CaimanOP
Module '"next-auth/next"' has no exported member 'getServerSession'.ts(2305)
"next": "^14.2.5",
"next-auth": "^5.0.0-beta.19",
Velvet ant
import { getServerSession } from "next-auth"; ?
@Velvet ant `import { getServerSession } from "next-auth";` ?
Spectacled CaimanOP
same thing
turns out import getServerSession from "next-auth"; exists
Velvet ant
I would not use the beta version
Velvet ant
it seems that is the auth() in v5
Spectacled CaimanOP
when i downgrade
Error: (0 , _server_authWEBPACK_IMPORTED_MODULE_3.auth) is not a function

yeah
Velvet ant
I thin downgrade will add a lot of changes, so stick with v5 and try auth()
Spectacled CaimanOP
i did originally use auth though
Velvet ant
what is your actual code ?
Spectacled CaimanOP
what part?
Velvet ant
all necessary part 🙂
@Velvet ant all necessary part 🙂
Spectacled CaimanOP
layout.tsx
import '@fortawesome/fontawesome-svg-core/styles.css';
import { config } from '@fortawesome/fontawesome-svg-core';
config.autoAddCss = false;

import { GeistSans } from "geist/font/sans";
import { Metadata } from "next";
import { auth } from "../server/auth";
import { SessionProvider } from "next-auth/react";
import "./globals.css";

export const metadata: Metadata = {
  title: "Guilds",
  description: "The best Discord listing site",
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await auth();

  return (
    <html lang="en">
      <body className={GeistSans.className}>
        <SessionProvider session={session}>
          <main>
            <div className="flex min-h-screen flex-col">{children}</div>
          </main>
        </SessionProvider>
      </body>
    </html>
  );
}


page.tsx
import HomePage from './servers/page';

export default async function Page() {
  return (
    <HomePage />
  );
}


HomePage.tsx
import { useState } from "react";
import { useSearch } from "../../hooks/useSearch";
import { useSearchQuery } from "../../hooks/useSearchQuery";
import { FooterView } from "../FooterView";
import { Navigation } from "../Navigation";
import { HeroPart } from "./HeroPart";

export default async function HomePage() {
    const [showBg, setShowBg] = useState<boolean>(false);
    const searchParams = useSearchQuery();
    const [search] = searchParams;
    const s = useSearch(search);
  
    return (
      <>
        <FooterView>
          <Navigation bg={showBg} />
          <div className='mb-16 sm:mb-24'>
            <HeroPart searchParams={searchParams} setIsSticky={setShowBg}></HeroPart>
          </div>
        </FooterView>
      </>
    );
  }
Navigation.tsx
"use client";

import { faDiscord } from "@fortawesome/free-brands-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffect, useState } from "react";
import { XMarkIcon, Bars3Icon } from "@heroicons/react/24/solid";
import { cn } from "../lib/utils";
import { NavigationProps, NavItemProps } from "../typing";
import { BlurEllipsis } from "./ui/BlurEllipsis";
import { Lightbar } from "./ui/Lightbar";
import { handleSignIn } from "./server/handleSignIn";
import { useSession } from 'next-auth/react';
import { ProfileDropdown } from "./ProfileDropdown";

export function Navigation(props: NavigationProps) {
  const [open, setOpen] = useState(false);
  const { data: session, status } = useSession();

  useEffect(() => {
    console.log(status, session);
  }, [status, session])

  function handleOpen() {
    setOpen((cur) => !cur);
  }

  useEffect(() => {
    window.addEventListener("resize", () => window.innerWidth >= 960 && setOpen(false));
  }, []);

  return (
    <>
      ...
      {!session ? (
      <form action={handleSignIn}>
        <button type="submit" className="...">
          <FontAwesomeIcon icon={faDiscord} className="..." />
          Login with Discord
        </button>
      </form>) : <ProfileDropdown />}
    </>
  )
}
ProfileDropdown.tsx
import { faGear, faSignOutAlt } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useSession } from "next-auth/react";
import { handleSignOut } from "./server/handleSignOut";
import { Menu, MenuDropdown, MenuHandler, MenuItem } from "./ui/Menu";

export function ProfileDropdown() {
  const { data: session } = useSession();

  if (!session) return <></>

  return (
    <Menu className="ms-2 z-[1000]">
      <MenuHandler>
        <img className="h-5 rounded-full" alt="Your Discord profile picture" src={session.user?.image ?? '/images/logos/default.png'} />
        <span className="text-sm">{session.user?.name ?? 'You'}</span>
      </MenuHandler>
      <MenuDropdown>
        <MenuItem>
          <a href="/manage" className="text-sm">
            <FontAwesomeIcon icon={faGear} className="mr-2" /> Manage
          </a>
        </MenuItem>
        <MenuItem>
          <form action={handleSignOut}>
            <button type="submit" className="text-sm">
              <FontAwesomeIcon icon={faSignOutAlt} className="mr-2" /> Logout
            </button>
          </form>
        </MenuItem>
      </MenuDropdown>
    </Menu>
  );
}
handleSignIn.ts
'use server'

import { signIn } from "../../server/auth";

export async function handleSignIn() {
    await signIn("discord");
}


handleSignOut.ts
'use server'

import { signOut } from "../../server/auth";

export async function handleSignOut() {
    await signOut();
}


server/auth.ts
import NextAuth from "next-auth"
import Discord from "next-auth/providers/discord"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Discord({
    clientId: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    authorization: `https://discord.com/api/oauth2/authorize?scope=${encodeURIComponent('guilds identify guilds.join')}`,
  })],
});
Velvet ant
your session client side is still empty ?
Spectacled CaimanOP
logging in works, logging out doesn't
@Velvet ant heres an example, i have to actually refresh my page for the username to be re-rendered as "login with discord"
Velvet ant
can you consol log something in your signout action
@Velvet ant can you consol log something in your signout action
Spectacled CaimanOP
anything?
Velvet ant
yes
Spectacled CaimanOP
not sure why After never logged
Velvet ant
can you try with the signOut client side ?

import { signOut } from "next-auth/react"

// ...
<MenuItem>
  <button onClick={() => signOut()}>
     <FontAwesomeIcon icon={faSignOutAlt} className="mr-2" /> Logout
  </button>
</MenuItem>
Answer
Velvet ant