Problem
I want to add events to a client component that has a server component as a parent component, but I get an error.
Message Error:
Error: Event handlers cannot be passed to Client Component props. <... className=... onChange={function}> ^^^^^^^^^^ If you need interactivity, consider converting part of this to a Client Component.
Things I've tried
- I created an "intermediary" file, we could call it, but as I imagined, the same thing happened.
- I tried to make the parent component the client component, but it asks me that it must be a server component.
- I tried to make the "Input" component a server component, but the problem is in the tag that comes by default from HTML, which is a client component.
Explication
I have a parent component that is a server component since it will make connections to the database, which is the following:
"use server"
import "./Filtro.css"
import { EntityMetadata, EntityTarget, ObjectLiteral } from "typeorm"
import { GetColumns } from "@/SQL/SQLManager"
import { Calendar } from "../Calendar/Calendar"
import { Circle } from "../Polygons/Circle/Circle"
import Image from "next/image"
import { ColumnSearch } from "./_components/ColumnSearch/ColumnSearch"
type FilterProps<T> = {
entity: EntityTarget<T>,
except?: string[]
}
export async function Filter<T extends ObjectLiteral>({ entity, except } : FilterProps<T>) {
const columns = (await GetColumns(entity))
.filter((column) =>
except != null ? except.indexOf(column.propertyName) : true
);
const changeEvent = (value: string) => {
console.log(value);
}
return (
<div className="filter-container">
<div className="flex items-center gap-3.5 mb-3.5">
{/* <div className="filter-circle"></div> */}
<Circle width={40} height={40} className="bg-primary"/>
<h2 className="text-2xl font-bold">Filtros</h2>
</div>
<div className="bg-section rounded-xl px-4 py-5">
{
columns.map((column) => {
return <ColumnSearch
entity={entity}
column={column}
className="bg-white text-black p-1.5 rounded-xl"
onInputChange={changeEvent}
/>
})
}
<div className="relative mt-3.5">
<input type="text" className="filter-input p-2.5 rounded-3xl" placeholder="Buscar"/>
<Image className="filter-magnifier" src="/white/svg/Magnifier.svg" width="24" height="24" alt="Magnifier"/>
</div>
</div>
</div>
)
}
But the object it calls (ColumnSearch) is also a server object, which can be either an Input or a Calendar, here is the code:
import { Calendar } from "@/components/Calendar/Calendar"
import { EntityTarget, ObjectLiteral } from "typeorm"
import { ColumnMetadata } from "typeorm/metadata/ColumnMetadata.js"
import './ColumnSearch.css'
import { Input } from "@/components/Input/Input"
type ColumnSearchProps<T> = {
entity: EntityTarget<T>,
column: ColumnMetadata,
className?: string,
onInputChange?: (value: string) => void
}
export async function ColumnSearch<T extends ObjectLiteral>({ entity, column, className, onInputChange } : ColumnSearchProps<T>) {
console.log(column.type)
return <div className='filter-option relative flex justify-between items-center cursor-pointer text-lg py-3.5'>
{column.propertyName.replace("_", " ")}
{
column.type != Date ?
<Input className={className}/> : //onChange={onInputChange ? (e) => onInputChange(e.target.value) : undefined}
<Calendar className={className}/>
}
</div>
}
But the component that calls ColumnSearch (Input) is actually an HTML input and this is a client component, the problem is that I want to add events to it but it won't let me because the parent component is a server component, here is the code:
"use client"
import { ChangeEventHandler, RefObject, useRef } from "react"
type InputProps = {
className?: string,
onChange?: ChangeEventHandler<HTMLInputElement>,
ref?: RefObject<HTMLInputElement>
}
export function Input({ className, onChange, ref } : InputProps) {
return <input className={className} onChange={onChange} ref={ref}/>
}
Answer
You can not.
Event handlers cannot be passed to Client Component, if you need interactivity, consider converting part of this to a Client Component.
In Nextjs, a server component can not pass a function to a client component.
Why not converting ColumnSearch to a client component? You can pass the data to your client component, and let it handle DOM logic.
If you want to keep Filter component as a server component responsible of fetching your data, it should not handle UI events..etc Let client components handle the UI.