Reputation: 557
I have a client component called CampaignTable. The CampaignTable component expects a columns object to render the columns. Inside my columns object I import a server component called CampaignActions. This is a simple dropdown menu in which I do an api call to get some data and use that data in my popover.
However, because the api call performed in the CampaignActions component, my page becomes very slow and I get an error saying:
Error: Server Functions cannot be called during initial render. This would create a fetch waterfall. Try to use a Server Component to pass data to Client Components instead.
However, to my understanding the component is already a React Server Component. I can solve the issue by removing the async and performing the api call in the top-level page and passing the data down as props such as:
Page > CampaignTable > CampaignActions.
But I wonder why my approach is wrong / causing issues. Because it would be nice if I could just perform the api call in the component that needs it, rather than passing it down.
This is my code
'use client'
import React from "react";
const CampaignTable = (props: CampaignTableProps) => {
const campaignColumns = useMemo(() => (
[
columnHelper.accessor("id", {
header: () => "ID",
cell: info => info.getValue()
}),
columnHelper.display({
id: "socials",
header: () => "Actions",
cell: ({row}) => {
return (
<CampaignActions {...row.original}/>
)
}
}),
]
), [])
return (
<DataTable
data={props.campaigns || []}
columns={campaignColumns}
/>
)}
My CampaignActions code
async function CampaignActions(props: Campaign){
const {data: clients} = await getAllClients().then((res) => res);
return (
<DataTableActions
edit={{
onEdit: async () => {
await openUpdateCampaignModal({
...props,
clients: clients ?? [],
})
}
}}
/>
)}
Upvotes: 17
Views: 23140
Reputation: 427
You just need to await the prefetch call in the RSC (Page) to get around this issue:
In the SRC (Page)
const bobPromise = queryClient.prefetchQuery({
queryKey: bobQueryKey,
queryFn: () => getBob(accessToken),
} as any);
const promises = [bobPromise];
await Promise.all(promises);
In the Client
const { data: bobData } = useSuspenseQuery({
queryKey: bobQueryKey,
queryFn: () => getBob(accessToken as any),
});
Boom, no error
Upvotes: 0
Reputation: 59
I faced this same issue and as the error suggests you can not use Server Functions during the initial render, my solution was to use Next Route API instead of server routes:
Before:
export default async function GetBlogList({ recent }: { recent: boolean }) {
const blogPosts = await fetchBlogPosts({ recent });
...
);
After:
export default async function GetBlogList({ recent }: { recent: boolean }) {
const [loading, setLoading] = useState(false);
const [blogPosts, setBlogPosts] = useState<any>(null);
useEffect(() => {
setLoading(true);
const fetchBlogPost = cache(async () => {
const blogPost = await axios.get(`/api/blog`);
setBlogPosts(blogPost);
setLoading(false);
});
fetchBlogPost();
}, []);
...
}
Upvotes: 1
Reputation: 5976
With regards to React Server Components, you can:
In your case, your CampaignActions
is a server component that tries to get all the clients asynchronicity, presumably a "server only" action. but your CampaignTable
is explicitly defined as a client component. Hence you are getting the error:
Error: Server Functions cannot be called during initial render. This would create a fetch waterfall. Try to use a Server Component to pass data to Client Components instead.
Read this (React Server Component RFC): https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md#capabilities--constraints-of-server-and-client-components and you will have a better idea how this works.
Upvotes: 2
Reputation: 83366
I think I figured this out after starting the bounty. The answer is hidden in plain sight.
Error: Server Functions cannot be called during initial render.
Server Functions, not to be confused with Server Components. I'm going to go out on a limb and assume that your
getAllClients()
function is in a file with "use server" at the top, making it a Server Action (or Server Function, as its called in the error message).
THAT is what cannot be called during initial render. Server Actions are intended to be called as part of user interactions. A user clicks a button, which calls a server action.
Server actions are special, and result in a network roundtrip. THAT is why React will not let you call them during initial render.
So have the function which fetches data not be a server action, and this should work for you.
Upvotes: 17