We are now ready to move to client components.
Let's start by creating a new components folder inside src:
mkdir src/componentsInside this folder we create a Posts.tsx file. Posts is a client component that will render the posts from the API:
Client components are defined by adding
"use client"to the top of the file.
src/components/Posts.tsx
"use client";
import type { Post } from "../services/schema";
export default function Posts({ posts }: { posts: readonly Post[] }) {
return (
<div>
{posts.map((post) => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</div>
);
}This client component receives posts as props from the server component, and it will render them on the client.
Inside src/pages/index.tsx we render Posts passing posts as a prop:
src/pages/index.tsx
export default async function HomePage() {
return (
<div>
<title>Index</title>
{await RuntimeServer.runPromise(
main.pipe(
Effect.match({
onFailure: Match.valueTags({
ParseError: (error) => <span>ParseError</span>,
RequestError: (error) => <span>RequestError</span>,
ResponseError: (error) => <span>ResponseError</span>,
}),
onSuccess: (posts) => <Posts posts={posts} />,
})
)
)}
</div>
);
}We now have the following:
- Effect services independent of both server and client components
- Server component with a
maineffect that executes theApiservice and rendersPosts - Client component that receives
postsas props and renders them on the client
But wait! We notice an error when running the code at this point:
Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
[{userId: 1, id: 1, title: ..., body: ...}, ..., ..., ...]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^