Closed kenhuang1964 closed 4 months ago
Hey @kenhuang1964 - with the code example that you have shared with us, it does not look like your component needs to be a client component – can you try removing the use client
from the top and let us know if that works?
Also it looks like your NavBar
component is a Server Component (it's marked as async), so that could be potentially an issue if you are marking the component as Client Component.
And here's a contrived example on how to call the server action from a client component:
// actions.ts
'use server';
import { signOut } from "@workos-inc/authkit-nextjs";
export const signOutAction = () => signOut();
// client-component.tsx
'use client';
import { signOutAction } from './actions.ts';
export function ClientComponent() {
return (
<form action={signOutAction}>
<button type="submit">Sign out</button>
</form>
);
}
Hey @lucasmotta, thank you for your help! I ended up still having to use a client component and the reason that I had async there was because I needed to use
const { user } = await getUser();
const signInUrl = await getSignInUrl();
const signUpUrl = await getSignUpUrl();
I created a new file called authUtils.ts:
"use server";
import {
getSignInUrl,
getSignUpUrl,
getUser,
signOut,
} from "@workos-inc/authkit-nextjs";
export async function getUserData() {
const { user } = await getUser();
const signInUrl = await getSignInUrl();
const signUpUrl = await getSignUpUrl();
return {
user,
signInUrl,
signUpUrl,
};
}
export const signOutAction = () => signOut;
and updated my client component code accordingly:
"use client";
import { UserButton } from "@clerk/nextjs";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@/components/ui/navigation-menu";
import { cn } from "@/lib/utils";
import React, { use, useEffect, useState } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { getUserData, signOutAction } from "@/utils/authUtils";
const components: { title: string; href: string; description: string }[] = [
{
title: "Technology",
href: "/topics/technology",
description: "Share your cool programming or engineering projects!",
},
{
title: "Art",
href: "/topics/art",
description: "Show off your art, music, or other creative projects!",
},
{
title: "Theater and Filmmaking",
href: "/topics/theater-and-filmmaking",
description: "Share and watch your films or plays!",
},
{
title: "March Term",
href: "/topics/march-term",
description: "Share your projects or cool experiences from March Term!",
},
{
title: "Sports",
href: "/topics/sports",
description: "Sport news.",
},
{
title: "Outdoors",
href: "/topics/outdoors",
description: "Nature and outdoor activities.",
},
{
title: "Other",
href: "/topics/other",
description: "Anything that doesn't fit in the other categories!",
},
];
export const Navbar = () => {
const [user, setUser] = useState<any>(null);
const [signInUrl, setSignInUrl] = useState<string>("");
const [signUpUrl, setSignUpUrl] = useState<string>("");
useEffect(() => {
const fetchUserData = async () => {
const { user, signInUrl, signUpUrl } = await getUserData();
setUser(user);
setSignInUrl(signInUrl);
setSignUpUrl(signUpUrl);
console.log("Sign In URL:", signInUrl);
console.log("Sign Up URL:", signUpUrl);
console.log("User:", user);
};
fetchUserData();
}, [signInUrl, signUpUrl, user]);
return (
<nav className="p-5 flex justify-around w-full shadow-md bg-muted">
<Link href="/">
<span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">
Athenian Creators Hub
</span>
</Link>
<NavigationMenu>
<NavigationMenuList>
<div className="flex items-center">
<NavigationMenuItem>
<Link href="/create-post">
<Button className="mr-4 bg-[#60A5Fa]">Share a Post</Button>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuTrigger>Navigation</NavigationMenuTrigger>
<NavigationMenuContent>
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]">
{components.map((component) => (
<ListItem
key={component.title}
title={component.title}
href={component.href}
>
{component.description}
</ListItem>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
</div>
<NavigationMenuItem>
<UserButton />
{user ? (
<form action={signOutAction}>
<Button className="mr-4 bg-[#60A5Fa]" type="submit">
Sign Out
</Button>
</form>
) : (
<Link href={signInUrl}>
<Button className="mr-4 bg-[#60A5Fa]">Sign In</Button>
</Link>
)}
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</nav>
);
};
const ListItem = React.forwardRef<
React.ElementRef<"a">,
React.ComponentPropsWithoutRef<"a">
>(({ className, title, children, ...props }, ref) => {
return (
<li>
<NavigationMenuLink asChild>
<a
ref={ref}
className={cn(
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
className,
)}
{...props}
>
<div className="text-sm font-bold leading-none">{title}</div>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
{children}
</p>
</a>
</NavigationMenuLink>
</li>
);
});
ListItem.displayName = "ListItem";
I am able to get the sign in and sign up working, but sign out is still not working. I am facing this error and I'm not sure how to resolve it because I have "use server" at the top of the file for authUtils.ts. This is the error:
⨯ Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
{: function}
^^^^^^^^
at stringify (<anonymous>)
I think it's closer now - in your signOutAction
you need to invoke the signOut()
:
export const signOutAction = () => signOut();
And I also created a simplified demo showing how to call a server action from a client component (I also moved the fetching of the user data to the server component): https://codesandbox.io/p/devbox/infallible-danilo-kzjvm4
Let me know if that helps.
I think it's closer now - in your
signOutAction
you need to invoke thesignOut()
:export const signOutAction = () => signOut();
And I also created a simplified demo showing how to call a server action from a client component (I also moved the fetching of the user data to the server component): https://codesandbox.io/p/devbox/infallible-danilo-kzjvm4
Let me know if that helps.
My glorious king it's working ❤️ I love u
Can I use sign out in a client component? I followed all the directions except I am using the sign out function in a client component and it doesn't work. I am getting this error:
I tried creating a new typescript file with "use server" at the top and imported the function from that file to the client component but it still doesn't work. Can someone please help me?
Here is my current code which requires "use client" at the top: