We are now ready to move to client components.
Let's start by creating a new components
folder inside src
:
mkdir src/components
Inside 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
main
effect that executes theApi
service and rendersPosts
- Client component that receives
posts
as 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: ...}, ..., ..., ...]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^