A little background
I'm using Shadcn UI library and Zod to create some simple forms in my Next.js React app. The inputs are of type select and text.
The problem
I'm following Shadcn documentation on how to set up the form with Zod and Shadcn's ui components but I get an error when the value of the text input is changing:
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen.
Worth mentioning
The error only occurs when changing the text (username) input value.
Also, the form is working properly despite the error.
What I've tried
Ive looked up the error and I found answers saying to add a value or defaultValue after the {...field} with a combination of a useState('') hook, which solved the error but now it won't let me submit the form.
Code
'use client'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { useForm } 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 { toast } from "@/components/ui/use-toast"
import Link from "next/link"
import { Input } from "@/components/ui/input"
import { ReactNode, useState } from "react"
const FormSchema = z.object({
email: z
.string({
required_error: "Please select an email to display.",
})
.email(),
username: z.string(),
})
const ZodForm = () => {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
})
function onSubmit(data: z.infer<typeof FormSchema>) {
console.log(data);
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a verified email to display" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="[email protected]">[email protected]</SelectItem>
<SelectItem value="[email protected]">[email protected]</SelectItem>
<SelectItem value="[email protected]">[email protected]</SelectItem>
</SelectContent>
</Select>
<FormDescription>
You can manage email addresses in your{" "}
<Link href="/examples/forms">email settings</Link>.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={'username'}
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<button type="submit">Submit</button>
</form>
</Form>
)
}
export default ZodForm
That occurs because you didn't set defaultValues to useForm.
So, the defaultValues is undefined.
then When You use the <FormField /> You probably write it like this:
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormDescription>This is your public display name.</FormDescription>
<FormMessage />
</FormItem>
)}
/>
Notice this line <Input {...field} />
The field is an object of ref, value, onChange, onBlur, disabled, and name.
The problem is the value is initialized by undefined then when you write any letter It's changed from undefined to string.
You can easily solve that by setting the default values
useForm({
defaultValues: {
username: ""
}
})
I was facing the problem like you and I resolved the problem by following the few steps:
const form = useForm({
//...
mode: 'onChange',
defaultValues: initialValue,
});
useEffect(() => {
if (props.data.id) {
getData(props.data.id).then((data) => {
form.reset(data);
setData(data);
});
}
}, [props.data.id, form]);
Hope that help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With