a little background
Im using shadcn ui library and Zod to create some simple forms in my Next.js React app.
The input 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 the {...field}
with a combination of a useState('')
hook, which solved the error but now it wont 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: ""
}
})