3

I'm working with Next.js and am testing their new way of data fetching but this can be a general React 18+ question too as the library itself is shifting towards a distinction between client and server components.

Suppose I have the setup:

// page.tsx (server component)
export default function Home() {
  return (
    <>
      <Search /> {/* Search is a client component that tracks state for an input */}
      <ServerData /> {/* ServerData is a server component that gets the initial data */}
    </>
  )
}

Since Search tracks the input state, how can I use that value and do a client-side filter on ServerData?

What I've tried: Search can be a client component that accepts a children prop. page.tsx can be restructured such that SearchData gets passed as a child to Search and ServerData can accept an input prop. This is likely not going to work since I cannot pass input to ServerData as Search only understands it as children.

  • 2
    Have you ever managed to find out how to do so? I'm basically having the same question. – Fares Apr 16 '23 at 18:15

2 Answers2

1

As I understand it, you're attempting to conditionally present server components based on some state within a client component. Next.js recommends passing server components as props to client components, which is sort of what you were attempting to do with the children prop. The problem with this approach is that the client component (in this case, <Search>) does not have access to any of the data associated with its individual children which it would need in order to perform filtering logic.

Since JSX can be passed as any prop, not just children, I recommend doing the following:

// ServerListItem.tsx
type ServerListItemProps = {
  message: string;
};

export default function ServerListItem({ message }: ServerListItemProps) {
  return <li>{message}</li>;
}
// ServerList.tsx
import ClientList from './ClientList';
import ServerListItem from './ServerListItem';

export default async function ServerList() {
  const data = await getData();

  const listItems = data.map((listItem) => ({
    message: listItem.message,
    content: <ServerListItem key={listItem.id} message={listItem.message} />,
  }));

  return <ClientList listItems={listItems} />;
}

const getData = () => Promise.resolve(serverData);
const serverData = [
  {
    id: 1,
    message: 'Item 1',
  },
  {
    id: 2,
    message: 'Item 2',
  },
  {
    id: 3,
    message: 'Item 3',
  },
];
// ClientList.tsx
'use client';

import React from 'react';

type ClientListProps = {
  listItems: {
    message: string;
    content: React.ReactNode;
  }[];
};

export default function ClientList({ listItems }: ClientListProps) {
  const [filterInput, setFilterInput] = React.useState('');

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFilterInput(e.target.value);
  };

  const filteredItems = listItems
    .filter((listItem) => listItem.message.includes(filterInput))
    .map((listItem) => listItem.content);


  return (
    <>
      <input onChange={handleChange} placeholder="filter" />
      <ul>{filteredItems}</ul>
    </>
  );
}

In this example, the <ServerList> component is responsible for telling React to render each <ServerListItem> component on the server and the props it should receive. It then passes a list of objects to <ClientList>, each containing the server-rendered result of <ServerListItem> as well as the message to be used for filtering. <ClientList> knows it will receive the list of objects and can perform client-side filtering to determine which objects' content to display.

You would then render the <ServerList> component in your page.tsx file in app/:

// page.tsx
import ServerList from './ServerList'

export default function Home() {
  return (
    <main>
      <ServerList />
    </main>
  );
}
slaymance
  • 46
  • 2
0

Server components can simply pass props to client components. Note that these props have to be serializable. Event handlers, for example, don't work consequently.

See also: https://nextjs.org/docs/getting-started/react-essentials#passing-props-from-server-to-client-components-serialization

Props passed from the Server to Client Components need to be serializable . This means that values such as functions, Dates, etc, cannot be passed directly to Client Components.

tristndev
  • 367
  • 2
  • 14