Search code examples
typescriptnext.jsshadcnuireact-server-components

Next.js 14: Server Component with ShadCN Tabs Repeatedly Fetches and Crashes App on Tab Change


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?


Solution

  • https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#unsupported-pattern-importing-server-components-into-client-components

    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