Functions cannot be passed directly to Client Components unless you explicitly expose it...
Answered
Luke posted this in #help-forum
LukeOP
Does anyone have experience with this error?
As far as I can tell, I am following the suggested architecture to pass in a server function to a client component.
Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.
As far as I can tell, I am following the suggested architecture to pass in a server function to a client component.
// nav.tsx
import Link from "next/link";
import { redirect } from "next/navigation";
import { createClient } from "@/lib/supabase/server";
import { Button } from "../ui/button";
import { NavigationMenu, NavigationMenuItem, NavigationMenuList } from "../ui/navigation-menu";
import { SignOut } from "../auth/sign-out";
export async function Navbar(): Promise<JSX.Element> {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
const signOut = async (): Promise<void> => {
"use server";
await supabase.auth.signOut();
return redirect("/login");
};
return (
<header className="max-w-screen-2xl mx-auto flex justify-between h-20 w-full shrink-0 items-center px-4 md:px-6">
<nav className="hidden w-full lg:flex gap-6 justify-between">
<NavigationMenu className="gap-x-4">
{user ? (
<NavigationMenuList>
<NavigationMenuItem>{JSON.stringify(user.email)}</NavigationMenuItem>
<NavigationMenuItem>
<SignOut signOut={signOut} />
</NavigationMenuItem>
<NavigationMenuItem>
<Button asChild>
<Link href="/dashboard">Dashboard</Link>
</Button>
</NavigationMenuItem>
</NavigationMenuList>
) : (
<NavigationMenuList>
<NavigationMenuItem>
<Button variant="link" asChild>
<Link href="/auth/login">Log in</Link>
</Button>
</NavigationMenuItem>
<NavigationMenuItem>
<Button asChild>
<Link href="/auth/signup">Sign up</Link>
</Button>
</NavigationMenuItem>
</NavigationMenuList>
)}
</NavigationMenu>
</nav>
</header>
);
}
// logout-button.tsx
import { Button } from "../ui/button";
interface ComponentProps {
signOut: () => void;
}
export function SignOut({ signOut }: ComponentProps): JSX.Element {
return (
<Button variant="link" onClick={signOut}>
Log out
</Button>
);
}
Answered by Luke
I realised I wasn't logging a line out for when I went to /auth/logout and realised I am blocking logged in users from accessing that route 🤦🏻♂️
62 Replies
I get that error on render.
But I get this when I click the button;
But I get this when I click the button;
i see
is your SignOut Button a clientside component?
You can pass a function from a Server Component to a Client Component, but, you should extract that sign out function logic to another file with the
use server
directive at the top, then import that function into the Client ComponentLukeOP
Well shit. That is precisely how the other actions work, too 🥴
@Luke Well shit. That is precisely how the other actions work, too 🥴
nope thats not the issue
show me the code of of your SignOut component
Yeah my mistake, literally right under that section is them doing exactly what you're doing.
@gin show me the code of of your SignOut component
LukeOP
all the code is right there ☝🏻
however, putting actions in there own file is much cleaner IMO and less error prone
u are passing a serverside function to a onclick callback
u cant do that
LukeOP
Is it because my main component is not a page itself?
@Luke Is it because my main component is not a page itself?
no, u cant pass a serverside function to a clientside component
u have to use serveractions or use route handlers
yeah instead of onClick={signout} try onClick={ () => signout }
that wont change anything
LukeOP
I need
formAction
I guessno you don't, you can call the server action outside of an action
Double-striped Thick-knee
why don't you export that function from separate file
LukeOP
Gin, I'm with you
https://github.com/vercel/next.js/blob/c039891908e0eab2e7dcc3d044b12a3fe12abf07/examples/with-supabase/app/login/page.tsx <-- this is how Supabase does the login, I think I need to do something similar instead of
onClick
but
just for simple logout i wouldnt recommend having a seperate serveraction for that
i would put it into a route handler and have it as a endpoint
they are using serveractions for login and signup which is good
LukeOP
Funny, that is exactly what I did earlier
// auth/logout/route.ts
"use server";
import { type NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/client";
import { defaultUrl } from "@/lib/constants";
export async function GET(request: NextRequest): Promise<NextResponse> {
const { searchParams } = request.nextUrl;
const next = searchParams.get("next") ?? "/";
const redirectTo = new URL(next, defaultUrl);
const supabase = createClient();
const { error: signOutError } = await supabase.auth.signOut({ scope: "global" });
if (signOutError) {
return NextResponse.redirect(redirectTo);
}
return NextResponse.redirect(redirectTo);
}
LukeOP
For some reason... this failed to actually affect the session
no errors, nothing. even after a hard refresh, the session gets reinitialised
So I looked more at the supabase demo, which led me back to what I am attempting to do now
i see
are you sure the signOut function executes correctly?
LukeOP
it only looks like that error is suppressed but I did log it earlier 😅
let me try it now
wait...
Figured it out
LukeOP
const anonymousPaths = [
"/auth/confirm",
"/auth/forgot-password/thanks",
"/auth/forgot-password",
"/auth/login",
"/auth/logout",
"/auth/signup",
"/auth/update-password",
];
/**
* Logged in users get directed to /dashboard if they hit any of these
*/
if (user && anonymousPaths.includes(url.pathname)) {
const redirectPath = url.clone();
redirectPath.pathname = "/dashboard";
return NextResponse.redirect(redirectPath);
}
@gin this is my middleware 😦 - spot the obvious mistake
@Double-striped Thick-knee am i missing something or did you intentionally didn't use "use client" at the top
LukeOP
I did not need that and it is not related to my issue. However, @gin is right I should use a route.
LukeOP
I realised I wasn't logging a line out for when I went to /auth/logout and realised I am blocking logged in users from accessing that route 🤦🏻♂️
Answer
LukeOP
well... shit 😂 fixed. thanks for rubberducking me gin
np
LukeOP
which message should i mark as the answer 😂
let me edit my question
LukeOP
fair enough. thanks