Getting page to refresh after a "signout" server action with Supabase
Answered
Charmantle posted this in #help-forum
I am currently working on implementing a user authentication system centered around Supabase. Currently, I have a navbar that will display an option to sign out if the user is currently signed in. This sign out function is currently being handled by a server action, the code for which is provided below:
The problem I am currently facing with this code is that upon the click of the sign out button, while the user is successfully signed out, the user needs to refresh the page manually in order to see the login and signup buttons in the navbar. If they don't manually refresh, the user can interact with the navbar and see their username, email, etc as if they were still still signed in.
I am very new to NextJS, but to my understanding from reading the documentation (though please correct me if I am wrong),
I've seen some stack overflow posts online regarding using the
The picture I have attached shows how I am getting the current user session (if that helps), but if there is any additional information that is needed please lmk!
"use server"
/**
Other server actions
**/
export async function signout() {
const supabase = await createClient()
const { error } = await supabase.auth.signOut()
if (error) {
redirect("/error")
}
revalidatePath("/", "layout")
redirect("/")
}
The problem I am currently facing with this code is that upon the click of the sign out button, while the user is successfully signed out, the user needs to refresh the page manually in order to see the login and signup buttons in the navbar. If they don't manually refresh, the user can interact with the navbar and see their username, email, etc as if they were still still signed in.
I am very new to NextJS, but to my understanding from reading the documentation (though please correct me if I am wrong),
revalidatePath
purges the client-side router cache so that on the next page visit we can recache the most up-to-date render of the page. My guess is that because this server action is being performed while the user is already in the root URL, the revalidation does not take effect (?). I've seen some stack overflow posts online regarding using the
useRouter
hook from next/navigation
and calling either the push
, replace
, or refresh
buttons, but they give me errors because you are not allowed to use hooks in server action code. I should also point out that the code that displays the UI allowing users to trigger a signout have the "use client"
directive.The picture I have attached shows how I am getting the current user session (if that helps), but if there is any additional information that is needed please lmk!
Answered by LuisLl
If you want to leverage Next.js server components try to leave all pages as server components and only make client components when you need client-side features or you need most hooks
39 Replies
Layouts don’t re render once they reached the client. They’re server components by default and don’t re render, which means they’re static.
You’re essentially purging the pages cache data, not the layout’s
You’re essentially purging the pages cache data, not the layout’s
For example, you could get the auth session directly in the server component without the need for an effect and state, just inline it:
const userSession = await supabase.auth.getUser();
For that you’ll need to clean up all the client code (state, effects), remove the “use client” directive and make your component async
const userSession = await supabase.auth.getUser();
For that you’ll need to clean up all the client code (state, effects), remove the “use client” directive and make your component async
Can I see the NavigationBar component?
@LuisLl Can I see the NavigationBar component?
of course, one sec
this popover component contains a button that invokes the sign out action
@LuisLl If you want to leverage Next.js server components try to leave all pages as server components and only make client components when you need client-side features or you need most hooks
Just making sure I am interpretting this correctly: My HomePage component currently uses the "use client" directive. But I should be leaving it as a server component and call Supabase's getUser within the navbar component. which is a client component?
You can call supabase’s getUser directly in the server component HomePage
Leave the client component for interactive purposes only, when you need react hook such as useEffect useState useRef.
If you have onClick events, or any other event handler, or when you access client only features such as the “window” object, local storage
If you have onClick events, or any other event handler, or when you access client only features such as the “window” object, local storage
If your “page” (which is denoted by page.tsx) is a server component it can access server side data and await async calls.
Also, you can use other features like:
- metadata
- caching
Also, you can use other features like:
- metadata
- caching
export default async function HomePage() {
const session = await supabase.auth.getUser();
return <></>;
}
I've never used supabase directly as a service for auth, do you have to instantiate the client every time?
Nvm you have, ignore my last question
My assumption is yes. For the login server actions (whether its with email or OAuth providers), the Supabase docs code snippets contain instantiations for the Supabase client.
Though I should look more into that
I just went to a repo linked in their docs and yes, that's the way it needs to be done
export default async function ProtectedPage() {
const supabase = await createClient();
const supabase = await createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return redirect("/sign-in");
}
return (<>....</>)
back to your main issue tho, you wanted your layout to update with the latest state when you logged out.
You can make the client component re-render with the new state from the server once you invalidate the path
You can make the client component re-render with the new state from the server once you invalidate the path
ah okok, ill give that shot
Let me know how it goes
@LuisLl Let me know how it goes
Alright, I think I got it. From the React Profiler and Components tool, I noticed two things that occur when the sign out server action is invoked:
1. The props to the navbar component change from a UserResponse object to null.
2. The entire navbar rerenders (whether or not this is something that should be avoided, I'm not totally sure)
In response to these observations, I have a state variable that stores the User data from the UserResponse prop (this can also be null). And then I have a useEffect hook that runs on every render and attempts to set the user state variable if the state value is null.
This seems to be working solution since the desired behavior has been achieved. My only concern is the navbar doing a rerender on the server action call, so I don't know if this counts as an unnecessary rerender, especially since I'm not really sure what's causing it. But I guess it works in my favor since it let's me use the useEffect hook.
1. The props to the navbar component change from a UserResponse object to null.
2. The entire navbar rerenders (whether or not this is something that should be avoided, I'm not totally sure)
In response to these observations, I have a state variable that stores the User data from the UserResponse prop (this can also be null). And then I have a useEffect hook that runs on every render and attempts to set the user state variable if the state value is null.
This seems to be working solution since the desired behavior has been achieved. My only concern is the navbar doing a rerender on the server action call, so I don't know if this counts as an unnecessary rerender, especially since I'm not really sure what's causing it. But I guess it works in my favor since it let's me use the useEffect hook.
nevermind, scrap the if (!user) check
everything else is the same though
When you sign out you clear the cookies from where you’re getting the user data on the server (from supabase)
Then the client component re renders with user = null
ooooh i see
@Charmantle Click to see attachment
You could just read the prop no need to keep duplicated on state
And setting a state inside the body of an effect is a bad practice it can cause lots of problems and debugging nightmares lol
Try to avoid the useState + useEffect pattern whenever possible, truly recommend that lol
@LuisLl You could just read the prop no need to keep duplicated on state
oh youre right 😅 whoops
yep everything works exactly the same even without the useEffect and useState 😄
Don’t worry you get used to the “not to do patterns” with a little practice
And if you let me, I would recommend you read the “You might not need an effect” page on React docs, it’s a little long article but it’s great and definitely changes the way you think about Effects
https://react.dev/learn/you-might-not-need-an-effect
https://react.dev/learn/you-might-not-need-an-effect
ill give that a read, thank you!
and thanks a lot for your help! you really saved me 😁
You’re welcome, hit me up if you need help.
Oh and mark the solution if you can! 😉
Oh and mark the solution if you can! 😉