admin管理员组文章数量:1188028
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 ?? [],
})
}
}}
/>
)}
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 ?? [],
})
}
}}
/>
)}
Share
Improve this question
asked Jul 25, 2023 at 9:52
Re-Angelo Re-Angelo
5372 gold badges8 silver badges23 bronze badges
2
|
4 Answers
Reset to default 15I 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.
With regards to React Server Components, you can:
- Import Client Components inside Server Components.
- Client Components can import "Server Components" as long as they can also run in the browser (they don't use Server-Only features).
- Client Components cannot import "Server Components" that use Server-Only features. (This is because any Component imported into a Client Component is forced to be treated as a Client Component.)
- Put Server Component as a child prop to a Client Component inside Server Component.
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.
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();
}, []);
...
}
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
本文标签:
版权声明:本文标题:javascript - React Server Component Error: Server Functions can not be called during initial render - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738385465a2084157.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
.then((res) => res)
does nothing; you can do justconst {data: clients} = await getAllClients();
. – Sophie Alpert Commented Oct 2, 2023 at 0:25