Do Server Components re-render when Client Components re-render? #70054
-
SummarySeveral resources online state that Server Components never re-render. However, the following MRE says otherwise. When a button is clicked, a Server Action runs, and then as a result, server-side log statements that only appear inside SC function bodies do fire off. That should technically not happen, as the SCs should be fixed and immutable within one request (for dynamic routes). MRE: https://github.com/magnusriga/server-action What am I missing here? SC: import { ClientComponent2 } from "./client-component2";
import { ClientComponent } from "./client-component";
import { headers } from "next/headers";
function wait(ms: number) {
return new Promise<number>((resolve) => setTimeout(() => {
const _headers = new Map(headers());
resolve(Math.floor(Math.random() * 100));
console.log('headers in promise after resolve', _headers);
}, ms))
}
export default async function Home() {
// const promise = wait(3000);
const value = await wait(3000);
console.log('headers in component body', new Map(headers())); // This runs again when ClientComponent2 re-renders.
return (
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
{/* <ClientComponent promise={promise}/> */}
<ClientComponent2 value={value}/>
</main>
</div>
);
} CC 'use client'
import { useEffect, useState } from "react";
import { setCookiesAction } from "@/action";
import { useRouter } from "next/navigation";
export function ClientComponent({promise}: {promise: Promise<number>}) {
const router = useRouter();
const [data, setData] = useState<number | null>(null);
useEffect(() => {
console.log('in useEffect, promise is', promise)
promise.then((data) => {
console.log('promise resolved inside useEffect, setting data state next, data is:', data)
setData(data)
})
}, [promise]);
async function actionHandler() {
console.log('before running SA')
const saReturn = await setCookiesAction(data ?? 0)
console.log('after SA, return value was', saReturn)
setData(saReturn)
// router.refresh()
}
return (
<div className="bg-black/[.05] dark:bg-white/[.06] p-4 rounded text-sm">
{data === null ? "Loading..." : `Random number: ${data}`}
<button onClick={actionHandler} className="block mt-4 p-2 bg-black/[.1] dark:bg-white/[.1] rounded text-white dark:text-black"> Click To Set Cookies</button>
</div>
);
} SA: 'use server'
import { revalidatePath } from "next/cache"
import { cookies } from "next/headers"
export async function setCookiesAction(num: number) {
cookies().set('name', 'value')
// revalidatePath('/')
return num
} Additional informationNo response Example |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
|
Beta Was this translation helpful? Give feedback.
-
Thank you for the link, @icyJoseph . I am not sure it always re-renders the SC. In my case, re-rendering the SC was actually the desired behavior, unlike the OP in the link you sent, but I ended up having to manually call Any idea why that could be? |
Beta Was this translation helpful? Give feedback.
-
I think I found the answer. In the Docs section on invalidating the Router Cache, it is stated that the Router Cache is invalidated when cookies are set/deleted in a SA. Meaning, next time any (soft) navigation is done within the app, a new request is sent to the server to get the new RSC payload. So, even though setting cookies invalidates the cache, it does not immediately send a new request to the server.
|
Beta Was this translation helpful? Give feedback.
I think I found the answer.
In the Docs section on invalidating the Router Cache, it is stated that the Router Cache is invalidated when cookies are set/deleted in a SA. Meaning, next time any (soft) navigation is done within the app, a new request is sent to the server to get the new RSC payload.
So, even though setting cookies invalidates the cache, it does not immediately send a new request to the server.
router.refresh()
, on the other hand, both invalidates the Router Cache and immediately sends a new request to the server so dynamic server components re-render.