Next.js Discord

Discord Forum

Async/Await Client Component

Unanswered
Scottish Fold posted this in #help-forum
Open in Discord
Scottish FoldOP
I am getting the following error.

async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 'use client' to a module that was originally written for the server.

Even though I have not used an async function as a component. After refreshing the page the error goes away.

Using next: 14.0.4

59 Replies

We can't help you if you don't provide the code for the component that's causing the error and how you structured your app. A general outline should be enough.

That said, sometimes barrel files can cause this type of issue if you import a server component from an index.ts file that also exports client components.
Scottish FoldOP
Can't provide the actual code since it is part of closed source org.

But here is the general structure

"use client"

import { useState, useEffect } from 'react';

function CampaignList() {
const [campaigns, setCampaigns] = useState([]);

useEffect(() => {
async function fetchCampaigns() {
const response = await fetch('/api/campaigns');
const data = await response.json();
setCampaigns(data);
}
fetchCampaigns();
}, []);

return (
<div>
{campaigns.map((campaign) => (
<div key={campaign.id}>
<h2>{campaign.title}</h2>
<p>{campaign.description}</p>
</div>
))}
</div>
);
}

export default CampaignList;
Hmmm... Since Next v13 making fetch requests to your own api endpoints is not the recommended way of getting data to your components. You can await a fetch call directly from a server component and pass down the list of campaigns.

But if for some reason you can't, or don't want to, try removing the async function and instead use the good old fetch().then().catch() logic to update your state after fetching the data.
No need for async/await
Just to check, but you didnt do
export default async function CampaignList()

right? I have doen that mistake a bunch of times and you might have forgotten to add it here
weird, any other components which use async await? chec how you call them
Scottish FoldOP
I have made similar components before but never experienced this issue
cause if you make a client component call a component you made with async, but without the "use client" it becomes a client component
No, it doesn't
Scottish FoldOP
I error mostly comes when the user navigates using the browser's back or forward button
no no, its the opposite.. if you call a server component inside of a client component, the server component becomes client
Hm, weird might be a nextjs bug then
try and reproduce it
and make a issue
Scottish FoldOP
Ok
@Arinji no no, its the opposite.. if you call a server component inside of a client component, the server component becomes client
Nooo... First of all you can't import a server component from a client component. And second, if you pass a server component to a client components as child it still remains a server component. Think of it like wrapping a layout in a context provider. The layout's children remain server components even though they are wrapped by a client component (the provider).
No, I don't see how navigating should affect your data fetching logic. At least not from the little code you shared.
Munchkin
Getting a bit confused by your code here.

Its should be export default function CampaignList()
But i see you have a different way of exporting. Could you try doing it like i just did?
@Scottish Fold Can't provide the actual code since it is part of closed source org. But here is the general structure "use client" import { useState, useEffect } from 'react'; function CampaignList() { const [campaigns, setCampaigns] = useState([]); useEffect(() => { async function fetchCampaigns() { const response = await fetch('/api/campaigns'); const data = await response.json(); setCampaigns(data); } fetchCampaigns(); }, []); return ( <div> {campaigns.map((campaign) => ( <div key={campaign.id}> <h2>{campaign.title}</h2> <p>{campaign.description}</p> </div> ))} </div> ); } export default CampaignList;
Silver Marten
i dont know why exactly you're getting this error but you can probably fix this by using server actions. just import your fetchCampagins function from a different file as a server action

// /actions.ts
"use server"

export async function fetchCampaigns() {
  const response = await fetch('/api/campaigns');
  const data = await response.json();
  return data;
}


import { fetchCampaigns } from "@lib/actions.ts"

export default function CampaignList() {
  const [campaigns, setCampaigns] = useState([]);
  
  useEffect(async () => {
    // invoke the server action
    const data = await fetchCampaigns();
    setCampaigns(data);
  }, []);
  
  return (
    <div>
      {campaigns.map((campaign) => (
        <div key={campaign.id}>
          <h2>{campaign.title}</h2>
          <p>{campaign.description}</p>
        </div>
      ))}
    </div>
  );
}
im not sure tho
Scottish FoldOP
Here's the error
@Munchkin Getting a bit confused by your code here. Its should be `export default function CampaignList()` But i see you have a different way of exporting. Could you try doing it like i just did?
How the component is exported is irrelevant and how he did it is equivalent to your suggestion. It's something much more subtle imo :thinq:
@not-milo.tsx Can you make a minimum reproduction repo so we can test it out and see where's the problem?
Scottish FoldOP
I was not able to reproduce it in a separate repo, it's working fine there, but the same code here is causing the error
The error comes when the user moves to the campaign page using the browsers back or front keys
As a temporary solution I have added an event listener that would trigger a reload in that case.
It's not the ideal way but since the deadline for the project delivery is approaching, I am now working on other tickets
Thank you all for the efforts 🙏
Silver Marten
ohhhhh right my bad
@Bighead carp How can you use async in useEffect
Silver Marten
then is it possible to solve this by revalidating the data that's fetched from /api/campaigns every x seconds and then just calling the fetchCampaigns() server action normally in the component like this

import { fetchCampaigns } from "@lib/actions.ts"

export default function CampaignList() {
  const campaigns = await fetchCampaigns();
  
  return (
    <div>
      {campaigns.map((campaign) => (
        <div key={campaign.id}>
          <h2>{campaign.title}</h2>
          <p>{campaign.description}</p>
        </div>
      ))}
    </div>
  );
}
Bighead carp
Is CampaignList a server or client component
@Scottish Fold Click to see attachment
Silver Marten
here it says its a client component but the one in the code i wrote is a server component
oh wait
you can't call server actions in a server component
Bighead carp
Right, then you can't export an async function as a client component
and you'll have to use useEffect or useMemo
Silver Marten
oh i see
thanks i understand it a bit more now
@Bighead carp Right, then you can't export an async function as a client component
Silver Marten
right so i was looking around and i figured that the solution for this type of problem would be to remove all the react hooks and just import the fetch function into a server component
interface Props {
  user: User
}

// Server component
export default async function TransactionList({ user }: Props) {
  // Server action
  const transactions: Transaction[] = await fetchAllTransactions(user);

  return (
    <div>
      {transactions.map((transaction, index) => (
        <TransactionCard key={index} transaction={transaction} />
      ))}
    </div>
  )
}

export async function fetchAllTransactions(user: User) {
  noStore()
  const id = user.id?.toString();
  try {
    const res = await sql<Transaction>`
        SELECT * FROM transactions
        WHERE user_id=${id}
        ORDER BY created_at DESC
      `
    const data = res.rows;
    return data;
  } catch (error) {
    console.log("Database error", error);
    throw new Error ("Failed to fetch all transactions.");
  }
}

but i also found that the same can be achieved by making fetchAllTransactions a server action. and i was just wondering if theres anything that can be achieved by making this a server action?
Bighead carp
Have you made sure you can't put the data into a server component with a suspense boundary?
Maybe thats better than fetching the data from the client side
Silver Marten
we're not fetching data from client side though
all of this code is run on the server
i think so at least im pretty new to this
Bighead carp
The title of this thread is async/await in client component
Silver Marten
oh i was just asking for my own clarification
my bad
Bighead carp
You dont need to make that fetchAllTransactions function a server action if its being executed on the server side
Silver Marten
oh alr
Bighead carp
I would suggest reading up on it
Silver Marten
will do