React Server Component Error: Server Functions can not be called during initial render
Asked Answered
P

4

13

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 ?? [],
                })
            }
        }}
    />
)}
Publicness answered 25/7, 2023 at 9:52 Comment(2)
By the way, .then((res) => res) does nothing; you can do just const {data: clients} = await getAllClients();.Firecrest
@SophieAlpert Yes that is true, I mistakenly put it there. Also wow, did not expect you to comment. Thanks for all your great work!Publicness
P
12

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.

Pea answered 1/10, 2023 at 20:58 Comment(0)
B
2

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.

Bosomy answered 6/10, 2023 at 14:25 Comment(0)
A
1

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();
  }, []);
  ...
}
Archaeozoic answered 4/1 at 6:20 Comment(0)
B
-4

The issue you're facing is due to the way React Server Components (RSC) work. They're not designed to fetch data during the initial render to avoid a "fetch waterfall" scenario, which can slow down your app's rendering.

In your case, CampaignActions is trying to fetch data during its initial render, hence the error. Even though it's a server component, it's still not allowed to fetch data at this stage.

A good way to solve this is to fetch the data higher up in your component tree and pass it down as props. This way, the data is fetched once and can be used by multiple components, improving performance. Alternatively, consider using a state management library like Redux or MobX to manage your app's state.

Your approach isn't necessarily wrong, but due to the constraints of RSCs, it's causing issues. Fetching data in the component that needs it might seem convenient, but it can lead to performance issues and more complex state management. So, it's generally better to fetch data at a higher level and pass it down as props. Hope this helps!

Bushmaster answered 25/7, 2023 at 10:27 Comment(2)
Oke thank you very much, makes a lot of sense!!Publicness
This answer is plainly incorrect. RSCs absolutely are designed to fetch data, directly, inside of their render body. The error message in question was about server functions not server componentsPea

© 2022 - 2024 — McMap. All rights reserved.