T
TanStack2y ago
rising-crimson

Validate on change only after the first submit

Hi, From my point of view, the best validation is done "on change" but only after the first submit. For instance, the first name must be minimum of 3 characters but I don't want to display an error as soon as the user starts typing their first character (or 2nd, which is still not enough to pass validation). However, once the user has tried to submit the form, and is fixing all the errors, I want live feedback = on change. What is the best way to implement this logic using this library? Thanks!
3 Replies
frozen-sapphire
frozen-sapphire2y ago
I am no expert, but one way to do this could possibly be to store an onSubmit state that starts as false and turns true only after a submit, and apply the validators only when it is true. Now the drawback to this approach is that it isn't handled by the library. There may be a better solution
rising-crimson
rising-crimsonOP2y ago
Thanks I willtry something like this but I think it should definitely be handled by the library before v1 :D. Most of the time it's good UX to NOT validate while the user is typing before the first time the user submits the form :).
frozen-sapphire
frozen-sapphire2y ago
I agree, maybe to either implement separate validators like: afterSubmitOnChange etc, or have a way to encapsulate the validators so they all happen "afterSubmit". The latter approach is maybe the better implementation so there wont be created many extra validators @Thomas B I found a really clean way to do this with the library while working on my reusable component. You just have to access submissionAttempts from the form state like this and then apply the onChange validation accordingly. Here is an example:
<Field
name="password"
validators={
useStore((state) => state.submissionAttempts) !== 0 && {
onChange: z
.string()
.min(9, "Password can't be less than 9 characters")
.max(20, "Password can't be more than 20 characters")
.refine(
(value) => /[A-Z]/.test(value),
'Password must include a capital letter',
)
.refine(
(value) => /\d/.test(value),
'Password must include a number',
),
}
}
children={(field) => {
return (
<input />
)
}}
/>
<Field
name="password"
validators={
useStore((state) => state.submissionAttempts) !== 0 && {
onChange: z
.string()
.min(9, "Password can't be less than 9 characters")
.max(20, "Password can't be more than 20 characters")
.refine(
(value) => /[A-Z]/.test(value),
'Password must include a capital letter',
)
.refine(
(value) => /\d/.test(value),
'Password must include a number',
),
}
}
children={(field) => {
return (
<input />
)
}}
/>
Here I am using zod for validation. The child input field is just for illustration. Hope this helps!

Did you find this page helpful?