T
TanStack11mo ago
other-emerald

debounce for synchronous validation?

This may be a silly question 🙂 I am wondering can I have a field level onChange validator operate on a debounce, without having to "manufacture" an async function? The validation will be against a zod schema, I just don't want the user to have a bad UX by getting immediate feedback
55 Replies
rare-sapphire
rare-sapphire11mo ago
You can set the number of milliseconds in onChangeAsyncDebounceMs and tanstack form will handle the debounce for you 🙂
other-emerald
other-emeraldOP11mo ago
Thanks for replying @Leonardo 🙂 So do you mean I should just put the zod schema on the onChangeAsync, even if the zod rule is not an async check? (Sorry for basic question)
rare-sapphire
rare-sapphire11mo ago
Give it a try if you haven't already 😄 Anyway doesn't seem wrong, if you declare that you validate after X time, technically it's no longer a sync operation
sensitive-blue
sensitive-blue5mo ago
No this doesn't work, sadly. Standard Schema validators (Zod, etc.) are not provided as async functions, so it would be nice if we could designate a debounce time for our syncronous validators.
conscious-sapphire
conscious-sapphire5mo ago
Wouldn't that mess with meta state such as isValidating? It's synchronous, but also takes a period of time depending on what you set as debounce ms
rare-sapphire
rare-sapphire5mo ago
Can you please share a stackblitz where it doesn't work? In the standard schema example you can safely do this:
const ZodSchema = z.object({
firstName: z
.string()
.min(3, '[Zod] You must have a length of at least 3')
.startsWith('A', "[Zod] First name must start with 'A'"),
lastName: z.string().min(3, '[Zod] You must have a length of at least 3'),
})

validators: {
onChangeAsyncDebounceMs: 500,
onChangeAsync: ZodSchema,
},
const ZodSchema = z.object({
firstName: z
.string()
.min(3, '[Zod] You must have a length of at least 3')
.startsWith('A', "[Zod] First name must start with 'A'"),
lastName: z.string().min(3, '[Zod] You must have a length of at least 3'),
})

validators: {
onChangeAsyncDebounceMs: 500,
onChangeAsync: ZodSchema,
},
sensitive-blue
sensitive-blue5mo ago
As far as I know you can only populate onChangeAsync with a solid single validator or async function. Since I am already performing an async validation I don’t think this would work for me. Maybe there’s an easy way to do both with debounce? I don’t know how isValidating is set, but debouncing a sync operation doesn’t require any promises so I’d assume no issue.
conscious-sapphire
conscious-sapphire5mo ago
just forked the standard schema example and passing a zod scchema to onChangeAsync works just fine but yeah, if you have other async validators unrelated to schemas it becomes an issue
sensitive-blue
sensitive-blue5mo ago
Would be cool if onChangeAsync could take an array… Probably a ridiculous thought
conscious-sapphire
conscious-sapphire5mo ago
an array of validators ... maybe? none of the validate types is an array, so it could probably be implemented as union depends on how errors are supposed to be handled though. What if array[0] caused errors and array[1] as well? flatten the array?
rare-sapphire
rare-sapphire5mo ago
If the library handles an array of async validators we'll get people expecting us to also handle concurrency, order, error handling (what if one fails?) & co
conscious-sapphire
conscious-sapphire5mo ago
true
rare-sapphire
rare-sapphire5mo ago
If you have multiple checks to do on async I'd say you can handle everything in one function and implement your own logic
conscious-sapphire
conscious-sapphire5mo ago
how do standard schemas work behind the scenes? do they also just reutrn a parsed object like you can with custom functions? if so, would a helper function for standard validators help perhaps? Something that can be called with a schema and it returns an error / null that tanstack parses as if you had given it the schema directly
rare-sapphire
rare-sapphire5mo ago
conscious-sapphire
conscious-sapphire5mo ago
is that accessible from outside this file though? the transformIssues I mean
rare-sapphire
rare-sapphire5mo ago
Let me doublecheck, now that we entirely removed adapters and allowed everything to be returned from validtors, transformIssues might be outdated and can be removed Ah no wait, that's also what spreads errors in the form 🤔 standardSchemaValidators seems to be accessible from user land, it might be enough
conscious-sapphire
conscious-sapphire5mo ago
looks like it works
No description
conscious-sapphire
conscious-sapphire5mo ago
at least I think I used validationSource right if the validation source can only be field or form, how about a method in formApi that validates its values with a schema as argument? formApi.somethingStandardSchemaNamedIdk(ZodSchema) the success check would just be what the snippet above is, checking if it's undefined
rare-sapphire
rare-sapphire5mo ago
Or even a one time validator, like you pass whatever you want on form.validateOnceWith(validator) No, that's too much 😂
conscious-sapphire
conscious-sapphire5mo ago
yeah, I wouldn't abstract too far it would just help for users that want special conditions to check instead of going the simple (and restricted) route of passing the schema directly then they needn't worry about turning it into validation issues because in the validation callback, both value as well as validationSource are redundant as they are inferrable from FormApi / FieldApi standardSchemaValidators would still be around if you truly really needed it outside of validators, I suppose I can make a PR for it later today if you'd like
rare-sapphire
rare-sapphire5mo ago
I can't find a decent name for this helper but I see value in the idea, yes please I'd love to discuss it on a PR!
conscious-sapphire
conscious-sapphire5mo ago
yeah, a name's difficult. If it's attached to formApi, then you can't guarantee it's only in context of validators and it doesn't change internal state
rare-sapphire
rare-sapphire5mo ago
Maybe looking at the implementation will help us figuring out the right name 😄
conscious-sapphire
conscious-sapphire5mo ago
GitHub
feat(FormApi): add helper method to parse values into Standard Sche...
As of tanstack form v1.4, there are two ways to use standard schemas as validators: 1. Pass directly validators: { onChange: mySchema } 2. Create a callback and import standardSchemaValidators va...
rare-sapphire
rare-sapphire5mo ago
Quick question, do we need to differentiate between sync and async?
conscious-sapphire
conscious-sapphire5mo ago
no, as the user decides where it will be used so they could just do async () => formApi.validateWithSchema()
rare-sapphire
rare-sapphire5mo ago
I mean if the schema is async
conscious-sapphire
conscious-sapphire5mo ago
oh I'm afraid I don't know. I haven't looked into standard schemas much. Perhaps I can answer that once I looked into how it actually works behind the scenes
conscious-sapphire
conscious-sapphire5mo ago
it might be trouble, yeah
No description
rare-sapphire
rare-sapphire5mo ago
One more challenge, we're doing it on field too? As with field you could simply use your schema and manually handle the errors. Speaking of form, don't we only need prefixSchemaToErrors to convert the object notation to how we define fields in form?
conscious-sapphire
conscious-sapphire5mo ago
could you elaborate on what you mean with your first point? standardSchemaValidators has the option to pass field as validationSource, so if that's not needed then the point of that parameter confuses me I guess for this?
const defaultFieldTransformer = (issues: readonly StandardSchemaV1Issue[]) =>
issues
const defaultFieldTransformer = (issues: readonly StandardSchemaV1Issue[]) =>
issues
rare-sapphire
rare-sapphire5mo ago
Exactly, that's inherited from the old validator adapters where you could actually pass a transform function But now that step is useless and can be removed
conscious-sapphire
conscious-sapphire5mo ago
true, but it could help readability. Otherwise you'll have to do schema['~standard'].validate(value)
rare-sapphire
rare-sapphire5mo ago
Ok, that's fair
conscious-sapphire
conscious-sapphire5mo ago
If the standard schema adapter has some stale code from before, then it could be refactored in the PR alongside those methods
rare-sapphire
rare-sapphire5mo ago
defaultFieldTransformer can be removed for sure 😄
conscious-sapphire
conscious-sapphire5mo ago
the zod user in me just wants to call it parseWithSchema, but I'm not sure if that's something the standard schema uses as word
rare-sapphire
rare-sapphire5mo ago
valibot uses parse too I think standard schema uses validate, do you think it's confusing in the form context? form.validateWithSchema.... not too convincing since we want to keep it stateless
conscious-sapphire
conscious-sapphire5mo ago
yeah, that's my main issue with it ChatGPT usually gives me solid ideas for names, but with this case it's mostly garbage how about diagnoseValuesWithSchema? inspectValuesWithSchema :HmmNotes: no, don't like that one
rare-sapphire
rare-sapphire5mo ago
getSchemaIssues? No get makes me think they're already there... I'm going back to parse
conscious-sapphire
conscious-sapphire5mo ago
that might be confused with getAllErrors() if someone passed a schema directly as validator I think while parse isn't specifically related to standard schema, it describes the best that it's just taking values and running it against the schema
rare-sapphire
rare-sapphire5mo ago
Yeah, I agree
conscious-sapphire
conscious-sapphire5mo ago
I will implement it with the placeholder name parseValuesWithSchema() for now. There's still stuff to check such as sync / async
rare-sapphire
rare-sapphire5mo ago
Sounds good, thanks!
conscious-sapphire
conscious-sapphire5mo ago
ooh, these methods actually help us narrow it a bit too since standardSchemaValidators returns ValidationIssue[] | { form: ..., fields: ...} | undefined but that's dependent on the source, which we know from formApi / fieldApi alright, that should also cover some docs to explain how the methods would work. I'm only familiar with react, so I'd appreciate someone to go through angular/solid/vue to check I didn't mistype it
rare-sapphire
rare-sapphire5mo ago
Aaand there's probably a typo in React 😄
conscious-sapphire
conscious-sapphire5mo ago
there is :PepeThumbs: of all places :curlUp: I just noticed something. The schema can technically set errors in places that don't even have fields. Could that cause issues? like if my schema expects name, but that field doesn't exist I'll just type it like validators does :PepeThumbs:
rare-sapphire
rare-sapphire5mo ago
Nitpicking on parseFieldValuesWithSchema, why Field in the name even if we don't pass a field name there?
conscious-sapphire
conscious-sapphire5mo ago
initially, it was to be consistent with other methods (FieldApi#swapValues -> FormApi#swapFieldValues) but you're absolutely right. Doesn't make sense in this context :Hmm: I guess it should just be parseValuesWithSchema?
rare-sapphire
rare-sapphire5mo ago
Yeah, that's probably better
conscious-sapphire
conscious-sapphire5mo ago
anything else that I could amend into this commit?
rare-sapphire
rare-sapphire5mo ago
Nah, I think I'm happy to merge after this
conscious-sapphire
conscious-sapphire5mo ago
sounds good. One sec there we go implemented in v1.5 :PepeThumbs:
sensitive-blue
sensitive-blue5mo ago
You are the 🐐 for this!

Did you find this page helpful?