Reputation: 97
I'm working on a Next.js 14 app that uses ShadCN for UI components and TanStack Query for data fetching. On the main screen, I have a Tabs component that allows users to switch between different sections. One of these sections is an async server component that makes a fetch request to retrieve and display a list of elements.
The Problem:
The server component is the default tab when the app loads. However, when I switch to another tab and then switch back, the server component starts making continuous fetch requests in a loop, which eventually crashes the app. After the crash, I see the following error:
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.
I tried to use the state of the Tabs selection to render only the selected tab, instead of letting the Tabs component manage the rendering naturally. This allowed me to navigate to the second tab, but when I tried to return to the original main tab, the same issue occurred.
The server component and a comented definition of the fetching function
import { getCandidates } from "@api";
import { TabsContent } from "@components";
// const url = process.env.NEXT_PUBLIC_API_URL;
// export const getCandidates = async (): Promise<Candidate[]> => {
// const response = await fetch(`${url}/v1/candidates`, { method: "GET" });
// return response.json();
// };
export default async function CandidatesContent() {
// const cand = await fetch(`${url}/v1/candidates`, { method: "GET" });
const candidates = await getCandidates();
console.log(candidates);
return (
<TabsContent value="candidates">
<div>
{candidates.map((candidate) => (
<p key={candidate.id}>{candidate.name}</p>
))}
</div>
</TabsContent>
);
}
The main page that contains the Tabs
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components";
import CandidatesContent from "@/components/candidatesContent/CandidatesContent";
import { useState } from "react";
export default function Home() {
const [activeTab, setActiveTab] = useState("candidates");
return (
<main className="container mx-auto py-4 px-6">
<Tabs value={activeTab} onValueChange={setActiveTab} className="">
<div className="flex justify-center">
<TabsList>
<TabsTrigger value="candidates">Candidates</TabsTrigger>
<TabsTrigger value="results">Results</TabsTrigger>
</TabsList>
</div>
<CandidatesContent />
<TabsContent value="results">
<div>results</div>
</TabsContent>
{/* {activeTab === "candidates" && <CandidatesContent />} */}
{/* {activeTab === "results" && (
<TabsContent value="results">
<div>results</div>
</TabsContent>
)} */}
</Tabs>
</main>
);
}
Question How can I prevent that server component to rerender continuosly the way it is currently doing? and why is this happening?
Upvotes: 0
Views: 1523
Reputation: 1
Same issue here: https://github.com/shadcn-ui/ui/issues/2741 You can use forceMount={true}, and then hidden when tab was not be selected. eg:
<TabsContent
forceMount={true}
value="withdraw"
hidden={"results" !== activeTab}
>
<div>results</div>
</TabsContent>
Explain: forceMount={true} is a property in Shadcn UI's Tabs component that forces all TabsContent components to be rendered when the component is mounted, rather than only when activated.
Specifically, by default, the content of the TabsContent component is rendered only when its corresponding TabsTrigger is activated. This means that if you switch to another tab, the unactivated TabsContent will be unmounted, which may cause the loss of your previously filled form data or state.
Using forceMount={true} ensures that all TabsContent are fully rendered and mounted in the DOM on initial load, even if they are not activated. This avoids data loss caused by content re-rendering.
Upvotes: 0
Reputation: 151
You are importing a server component into a client component, which is not supported. You should either turn Home() into a server component or turn the server component into a client one
Upvotes: 1