I'm working on a React project using react-hook-form along with the Controller component to manage a form with Select components. However, I'm running into some issues:
When I submit the form without interacting with the Select components, everything works as expected, and the default values are submitted correctly.
If I interact with any Select component (e.g., propertyType) and make a selection, the value for that field in the submitted form data becomes an empty string. Placeholder Issue:
The SelectTrigger does not show the default value or selected option. It only shows the placeholder text ("Select a property type") after making a selection, and even then, it does not display the correct option.
My Code :
"use client";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
const formSchema = z.object({
location: z.string().min(1, "Location is required"),
propertyType: z.string(),
purpose: z.string(),
priceRange: z.string(),
beds: z.string(),
filters: z.string(),
});
const propertyTypeOptions = [
{ value: "residential", label: "All in Residential" },
// Add more options as needed
];
export default function PropertyFilter() {
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
location: "",
propertyType: propertyTypeOptions[0].value,
purpose: "rent",
priceRange: "any",
beds: "any",
filters: "baths-area",
},
});
function onSubmit(values) {
console.log(values);
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="form-container">
<FormField
control={form.control}
name="propertyType"
render={({ field }) => (
<FormItem>
<FormLabel>Property Type</FormLabel>
<FormControl>
<Controller
name="propertyType"
control={form.control}
render={({ field }) => (
<Select
onValueChange={field.onChange}
value={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a property type" />
</SelectTrigger>
<SelectContent>
{propertyTypeOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<button type="submit">Submit</button>
</form>
</Form>
);
}
The SelectTrigger should display the selected option or default value correctly. The submitted form data should include the correct value from the Select component after making a selection.
The SelectTrigger initially does not show any text. After selecting an option, the form data contains an empty string for that field.
My updated approach :
"use client";
import React from "react";
import { Controller, Control, FieldValues, Path } from "react-hook-form";
import { Input } from "@/components/ui/input";
import {
FormItem,
FormLabel,
FormControl,
FormMessage,
} from "@/components/ui/form";
interface InputFieldFormProps<T extends FieldValues> {
name: Path<T>;
label: string;
placeholder: string;
control: Control<T>;
required?: boolean;
type?: string;
className?: string;
is_supported_rtl: boolean;
}
const InputFieldForm = <T extends FieldValues>({
name,
label,
placeholder,
control,
required = false,
type = "text",
className = "",
is_supported_rtl,
}: InputFieldFormProps<T>) => {
return (
<FormItem className={`w-full space-y-2 ${className}`}>
<FormLabel>{label}</FormLabel>
<FormControl>
<Controller
name={name}
control={control}
render={({ field, fieldState }) => (
<>
<Input
placeholder={placeholder}
type={type}
required={required}
className="h-[42px] rounded-[10px] border border-[#E9E9E9] bg-white px-[15px] py-[10px]"
{...field}
value={field.value ?? ""}
dir={is_supported_rtl ? "rtl" : "ltr"}
onChange={(event) => {
const value = event.target.value;
if (type === "number") {
const numValue = parseFloat(value);
field.onChange(isNaN(numValue) ? "" : numValue);
} else {
field.onChange(value);
}
}}
/>
<FormMessage className="text-primary">
{fieldState.error?.message}
</FormMessage>
</>
)}
/>
</FormControl>
</FormItem>
);
};
export default InputFieldForm;
I just encountered the exact same issue as you described with the exact same use case. It looks like I customised the SelectItem
component within components/ui/select.tsx
. I removed the SelectPrimitive.ItemText
that is by default, wrapping the children
.
I think you should check if you didn't do the same mistake