Im using https://ui.shadcn.com/docs/components/form components for my nextjs13 app. I want to either dynamically set the input fields value to the edited goal or when I click the "add" button to type a value in the field.
Currently this works fine except the zod validation doesnt know about my form values?
I get "Required" as an error message under my input field. How can I let zod know the form values when using value={} and onChange={} props in chadcn Input fields?
'use client';
import React, { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
import { Button } from '@/components/ui/button';
import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { toast } from '@/components/ui/use-toast';
import ButtonLoading from './ui/ButtonLoading';
import { useState } from 'react';
import { GoalType } from '@/types/goal';
interface GoalDayFormProps {
editedGoal: GoalType | null;
setEditedGoal: React.Dispatch<React.SetStateAction<GoalType | null>>;
}
const GoalDayForm = ({ editedGoal, setEditedGoal }: GoalDayFormProps) => {
const [loading, setLoading] = useState(false);
const [addingGoal, setAddingGoal] = useState(false);
const [newGoalText, setNewGoalText] = useState('');
useEffect(() => {
if (editedGoal) {
setAddingGoal(false);
}
}, [editedGoal]);
const FormSchema = z.object({
goal: z.string().min(1, {
message: 'The goal field cannot be empty.',
}),
});
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (editedGoal) {
setEditedGoal((prev: any) => ({ ...prev, text: e.target.value }));
}
setNewGoalText(e.target.value);
};
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>
),
});
if (editedGoal) {
setEditedGoal(null);
}
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="mt-[1.5rem] w-full space-y-4">
{(addingGoal || editedGoal) && (
<FormField
control={form.control}
name="goal"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
placeholder="Goal description"
{...field}
value={editedGoal ? editedGoal.text : newGoalText}
onChange={(e) => handleInputChange(e)}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<div className="flex gap-4">
{addingGoal && (
<>
<Button
variant="ghost"
disabled={loading}
onClick={() => setAddingGoal(false)}
>
Cancel
</Button>
<ButtonLoading loading={loading} type="submit">
Add goal
</ButtonLoading>
</>
)}
{editedGoal && (
<>
<Button
variant="ghost"
disabled={loading}
onClick={() => setEditedGoal(null)}
>
Cancel
</Button>
<ButtonLoading loading={loading} type="submit">
Edit goal
</ButtonLoading>
</>
)}
{!addingGoal && !editedGoal && (
<Button onClick={() => setAddingGoal(true)}>New Goal</Button>
)}
</div>
</form>
</Form>
);
};
export default GoalDayForm;