wyc
wyc

Reputation: 55293

Dilemma with server component inside client component

PersonsFetcher is a server component that fetches data from an API then passes that to a client component to present the data interactively (filtering, sorting, etc). The other elements, Button and PersonModal (has a form to create a new person), don't need fetched data to work, so I placed them outside of PersonsFetcher.

'use client';

import { Button } from '@/components/ui/button';
import { ListPlus } from 'lucide-react';
import PersonModal from '@/components/PersonModal';
import { useState } from 'react';
import PersonsFetcher from '@/components/PersonsFetcher';

export default function PersonsPage() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setIsModalOpen(true)}>
        <ListPlus className="mr-2 h-4 w-4" /> Add new resource
      </Button>
      <Suspense fallback={<div>Loading...</div>}>
        <PersonsFetcher />;
      </Suspense>
      <PersonModal
        isOpen={isModalOpen}
        setIsOpen={setIsModalOpen}
      />
    </>
  );
}

As you can see, 'use client' will make <PersonsFetcher> a client component (therefore, producing an error in Next.js). But I need 'use client' in order to use useState.

How to solve this dilemma?

Upvotes: 1

Views: 203

Answers (1)

KcH
KcH

Reputation: 3502

Not totally sure if this works or if it so its the right approach at all tbh 🤞 ... how about a new client component with its own state for button and modal and then a new server component to render both of these client component and the server component PersonsFetcherwe had ..

New client component (as it can be standalone to render inside server comp):

'use client';

import { Button } from '@/components/ui/button';
import { ListPlus } from 'lucide-react';
import PersonModal from '@/components/PersonModal';
import { useState } from 'react';

export default function PersonsFormModal() {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setIsModalOpen(true)}>
        <ListPlus className="mr-2 h-4 w-4" /> Add new resource
      </Button>
      <PersonModal
        isOpen={isModalOpen}
        setIsOpen={setIsModalOpen}
      />
    </>
  );
}

and a New server component to render them (since server comp can render the client but not vice versa)

import PersonsFetcher from '@/components/PersonsFetcher';
import PersonsFormModal from '..';
export default function PersonsPage() {

  return (
    <>
      <PersonsFormModal/>
      <Suspense fallback={<div>Loading...</div>}>
        <PersonsFetcher />;
      </Suspense>
    </>
  );
}

Upvotes: 1

Related Questions