T
TanStack•4mo ago
stormy-gold

Form not submitting when adding onChange zod validator

I'm trying to add zod (v4) schema validation on the boundary between my forms/trpc interface. When I add an onChange validator my form stops submitting, the form state appears to think it's submitting since canSubmit transitions to false in the form.Subscribe, however, the onSubmit callback doesn't get called. When I remove the onChange validator in place of manually calling zodSchema.parse() in the onSubmit is called, the schema validated, and my rpc executes. Any ideas on how to debug this or what I'm doing wrong?
const form = useForm({
defaultValues: {
email: user!.email,
first_name: profile?.first_name || "",
last_name: profile?.last_name || "",
job_title: profile?.job_title || "",
target_market: "",
note: getInitialNote(leadType, contextInfo),
type: leadType,
},
validators: {
onChange: SubmitLeadSchema,
},
// this doesn't execute unless onChange is removed
onSubmit: async ({ value }) => {
try {
console.log("inside try")
const res = SubmitLeadSchema.parse({ ...value, brand_id: brandId });
console.log(res)
await trpc.leads.submit.mutate({ ...value, brand_id: brandId });
onClose();
toast.success("Your request has been sent to the brand!");
} catch (error) {
if (error instanceof TRPCError) {
console.error(error);
return;
}
if (error instanceof Error) {
console.error(error);
return;
}
console.error(error);
return;
}
},
});
const form = useForm({
defaultValues: {
email: user!.email,
first_name: profile?.first_name || "",
last_name: profile?.last_name || "",
job_title: profile?.job_title || "",
target_market: "",
note: getInitialNote(leadType, contextInfo),
type: leadType,
},
validators: {
onChange: SubmitLeadSchema,
},
// this doesn't execute unless onChange is removed
onSubmit: async ({ value }) => {
try {
console.log("inside try")
const res = SubmitLeadSchema.parse({ ...value, brand_id: brandId });
console.log(res)
await trpc.leads.submit.mutate({ ...value, brand_id: brandId });
onClose();
toast.success("Your request has been sent to the brand!");
} catch (error) {
if (error instanceof TRPCError) {
console.error(error);
return;
}
if (error instanceof Error) {
console.error(error);
return;
}
console.error(error);
return;
}
},
});
5 Replies
stormy-gold
stormy-goldOP•4mo ago
I'm also just noticed that I'm having problems with the reactivity of the form.Subscribe as well, if the form is invalid, then I enter a field value to validate the field (the field.state.meta.errors clean up) but the submit button still has a false value for canSubmit
passive-yellow
passive-yellow•4mo ago
You're just collecting primitive types (strings), right? Can you show more code? The Schema, etc. or even better create a Stackblitz reproduction? For now all I see is the user!.email which I'd change to user?.email || "" You might get some more info in the onSubmitInvalid where you can check what values where submitted
stormy-gold
stormy-gold•4mo ago
canSubmit refers to whether there are errors in your form or not (and therefore if you can submit). Did you mean to check for isSubmitting instead? The form onSubmit only gets called if all validation passes. as for this, form level schemas validate the whole form. So cleaning up one field could mean you have another field that is still in an error state
passive-yellow
passive-yellow•4mo ago
For debugging I like to paste this Code into my JSX:
<form.Subscribe
selector={(state) => state}
children={(data) => <pre>{JSON.stringify(data, null, 2)}</pre>}
/>
<form.Subscribe
selector={(state) => state}
children={(data) => <pre>{JSON.stringify(data, null, 2)}</pre>}
/>
This way you can change what you'll see by just updating the 2nd state in the selector E.g.: selector={(state) => state.errorMap} Or: selector={(state) => [state.canSubmit, state.isValid]}
stormy-gold
stormy-goldOP•4mo ago
@ksgn thank you for the tip that immediately let me see what I did wrong, my schema had a required field (brand_id) which wasn't set by the form, easy one to fix once I saw the full errors 🙂 thanks!

Did you find this page helpful?