I’m using NextJS 14, Zod and ShadCN UI which uses React-Hook-Form.
I’m in a scenario where I’m creating a form that can be used for both ‘create’ and ‘update’. I pass values into the form and set defaultValues with those -or- empty strings for the ‘create’ form. But, as some of the fields are mandatory, an empty string is still a value and sidesteps the normal form field validation. On a text input I can add a .min(1, {message: ‘some message’}) but I also have a dropdown that I’m struggling on to enforce a mandatory value. Ideally I can leave the ZOD schema clean, without the .optional() addition and the form will manage necessary fields for me.
Recently I decided to set the default values for mandatory fields to ‘undefined’ and this solved my mandatory fields issue, but when entering data into the one of these fields I get an error letting me know that the form is moving from uncontrolled to controlled – due to ‘undefined’ as a starting value.
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.
What’s the best way to accomplish this and still have mandatory fields?
— ZOD SCHEMA —
export const AddressFormSchema = z.object({
form_type: z.string().optional(),
id: z.string().optional(),
type: z.string(),
line1: z.string().trim().toLowerCase(),
line2: z.string().trim().toLowerCase().optional(),
city: z.string().trim().toLowerCase().optional(),
state: z.string().optional(),
postal_code: z
.union([
z.string().length(0, {
message: "No more than 10 digits",
}),
z.string().trim().max(10),
])
.optional(),
agency_id: z.string().optional(),
contact_id: z.string().optional(),
supplier_id: z.string().optional(),
created_at: z.string().optional(),
updated_at: z.string().optional(),
});
-FORM PAGE—
const { update } = props;
const defaultValues = {
form_type: update ? "update" : "create",
id: update ? props.address?.id : undefined,
type: update ? props.address?.type : undefined,
line1: update ? props.address?.line1 : undefined,
line2: update ? props.address?.line2 || "" : "",
city: update ? props.address?.city || "" : "",
state: update ? props.address?.state || "" : "",
postal_code: update ? props.address?.postal_code || "" : "",
};
const form = useForm<AddressFormSchemaType>({
resolver: zodResolver(AddressFormSchema),
defaultValues: defaultValues,
});