T
TanStack•5mo ago
like-gold

When is the schema validation ACTUALLY happening?

I encountered this issue - I have an age, that should be a number after submitting. However, to get it, I need to use TextField. Because of Typescript errors, I need to cast it as number (I know this isn't the best approach, but hear me out pls 😄 ) When I hit submit, the age value is run through the schema (which happens and logged REFINED VALUE is of type number). However, when I log the the value and its type in onSubmit handler, it is a string. Isn't this weird? In zod, when I run values through a schema, I can count on them being the correct type, but here it seems it doesn't work that way. It seems like the result of the parsing is thrown away and the original values are returned in the onSubmit handler. Is this a bug or a required behavior?
const schema = z.object({
age: z.coerce.number().min(18)
.refine((val) => {
console.log('REFINED VALUE: ', typeof val, val)
return true
})
})

type InputType = z.input<typeof schema>

const TanstackFormExample = () => {
const defaultValues: InputType = {
age: 12
}

const form = useForm({
defaultValues,
validators: {
onSubmit: schema
},
onSubmit: ({ value }) => {
console.log('ON SUBMIT: ', typeof value.age, value.age)
}
})

return (
...
<form.Field name='age'>
{(field) => (
<TextInput
placeholder='Age'
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value as unknown as number)}
onBlur={field.handleBlur}
error={field.state.meta.errors[0]?.message}
/>
)}
</form.Field>
...
)
}
const schema = z.object({
age: z.coerce.number().min(18)
.refine((val) => {
console.log('REFINED VALUE: ', typeof val, val)
return true
})
})

type InputType = z.input<typeof schema>

const TanstackFormExample = () => {
const defaultValues: InputType = {
age: 12
}

const form = useForm({
defaultValues,
validators: {
onSubmit: schema
},
onSubmit: ({ value }) => {
console.log('ON SUBMIT: ', typeof value.age, value.age)
}
})

return (
...
<form.Field name='age'>
{(field) => (
<TextInput
placeholder='Age'
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value as unknown as number)}
onBlur={field.handleBlur}
error={field.state.meta.errors[0]?.message}
/>
)}
</form.Field>
...
)
}
17 Replies
like-gold
like-goldOP•5mo ago
If anyone could help me figure it out how it works I would really appreciate it 😅 having the value in the onSubmit not validated feels straight up weird
adverse-sapphire
adverse-sapphire•5mo ago
onSubmit received the input while it respects validation in transforms and refinements, it will not preserve that value as of now so you‘ll have to add a line where it parses the incoming value with the same schema the main conflict for passing a schema output to onsubmit is which validator are you gonna pick if a field validators has a transform as well as a form validator, which will it respect
like-gold
like-goldOP•5mo ago
Thanks a lot, I get it now. Is this information somewhere in the docs? As someone who is transitioning from RHF to Tanstack Form, the separation of validation and transformation is not something I would expect and it might be benefitial to add it to the documentation somewhere
noble-gold
noble-gold•5mo ago
I don't think its mentioned but that it should be This would be where I'd be looking for it: https://tanstack.com/form/latest/docs/framework/react/guides/validation Or here (from the headline, the content doesn't match it at the moment): https://tanstack.com/form/latest/docs/framework/react/guides/submission-handling (well I don't get any of what this is trying to tell me 🙈 - this will also log Rick - [object Object], right?)
like-gold
like-goldOP•5mo ago
My thoughts exactly 😄
adverse-sapphire
adverse-sapphire•5mo ago
yeah, that title seems misleading
noble-gold
noble-gold•5mo ago
So there should a a transforms object on the FormApi?
useForm({
defaultValues: {
firstName: '',
lastName: '',
},
validators: {
onSubmit: MyFormSchema // makes sure the form is valid
},
transformers: {
onSubmit: MyFormSchema // transforms the data
},
onSubmit: async ({ value }) => { // <-- `value` will be typed as the Output of MyFormSchema
console.log(value)
},
})
useForm({
defaultValues: {
firstName: '',
lastName: '',
},
validators: {
onSubmit: MyFormSchema // makes sure the form is valid
},
transformers: {
onSubmit: MyFormSchema // transforms the data
},
onSubmit: async ({ value }) => { // <-- `value` will be typed as the Output of MyFormSchema
console.log(value)
},
})
adverse-sapphire
adverse-sapphire•5mo ago
might get confusing since the nextjs adapter already implements a transform property
noble-gold
noble-gold•5mo ago
Validators validate, Transformers transform Transformers only on FormApi?
adverse-sapphire
adverse-sapphire•5mo ago
but if you implement a transformers object, then onSubmit could‘ve just done the same no?
noble-gold
noble-gold•5mo ago
Could, but then Validators would also Transform… maybe you want to validate stuff first and them transform them differently? Some Data is incoming => is valid? => if yes: transform the data to how we need it for processing
adverse-sapphire
adverse-sapphire•5mo ago
yeah …
noble-gold
noble-gold•5mo ago
If you're not using a library this could be handy I think… Or even with a Library this gives you extra control So you'll have a MyValidationSchema and a MyTransformationSchema (where you can be sure that it get's valid data that passed MyValidationSchema)
adverse-sapphire
adverse-sapphire•5mo ago
do you perhaps have a zod implementation example of what you‘d think this looks like? or a different schema library because what I meant in the earlier comment is this: * Pass a schema to transform -> parse the value in onSubmit with 1 line instead * Custom callback function -> call it in onSubmit
generous-apricot
generous-apricot•5mo ago
I'm facing the same as you, I thought that the value in onSubmit was the processed data of the schema (including the transformation), in my case, I have a string with multiple values separated by ',' and then I apply a schema transformation to convert the string to an array of strings. What I'm doing so I can continue the implementation is parsing the value against the schema again to get the result transformed
onSubmit: ({ value }) => {
console.log('value >>', value);
const parsed = v.parse(MultipleUrlEvaluationFormSchema, value);
console.log('parsed >>', parsed);
}
onSubmit: ({ value }) => {
console.log('value >>', value);
const parsed = v.parse(MultipleUrlEvaluationFormSchema, value);
console.log('parsed >>', parsed);
}
noble-gold
noble-gold•5mo ago
How is it done with validators? Fields first then Form? Couldn't it be the same here? run all the field.onSubmit and then the form.onSubmit, for all of them use the resulting output?
adverse-sapphire
adverse-sapphire•5mo ago
field validator errors take precedence over form validator errors if the field schema transforms it into a number, then the form schema would receive a number when the input expected a string so that‘d cause conflicts

Did you find this page helpful?