T
TanStack4mo ago
metropolitan-bronze

RHF's onTouched mode

Is it possible to mimic the behavior of React Hook Form's onTouched mode? There doesn't appear to be an equivalent option for validators. https://www.react-hook-form.com/api/useform/#mode
useForm
Performant, flexible and extensible forms with easy-to-use validation.
11 Replies
exotic-emerald
exotic-emerald4mo ago
are you using field-level validators or form-level validators? the easiest way to mimic this is to use a form-level onChange validator, and only show errors in fields if isBlurred: true
metropolitan-bronze
metropolitan-bronzeOP4mo ago
form-level validators
exotic-emerald
exotic-emerald4mo ago
isBlurred is currently a one-way change, it won't be set to false if the field isfocused
const form = useForm({
// ...
validators: {
onChange: // ...
}
})

<form.Field name="foo">
{field => <>
{/* ... */}
{field.state.meta.isBlurred && !field.state.meta.isValid && (
<>{/* Display field.state.meta.errors */}</>
)}
</>}
</form.Field>
const form = useForm({
// ...
validators: {
onChange: // ...
}
})

<form.Field name="foo">
{field => <>
{/* ... */}
{field.state.meta.isBlurred && !field.state.meta.isValid && (
<>{/* Display field.state.meta.errors */}</>
)}
</>}
</form.Field>
metropolitan-bronze
metropolitan-bronzeOP4mo ago
Your example is very similar to what I have atm. The only thing missing behavior-wise is: when you focus on a field, make no changes, then focus out. No error message appears, because no validations have ran due to no change event. Where as with RHF's onTouched mode, the error would appear, because validations are initially triggered on the first blur event. After that, it switches to being triggered on every change event.
exotic-emerald
exotic-emerald4mo ago
I see. This behaviour should only happen on the initial blur before changes have happened, right?
metropolitan-bronze
metropolitan-bronzeOP4mo ago
That's correct
exotic-emerald
exotic-emerald4mo ago
I suppose this may warrant another validateLogic implementation. We currently have one emulating revalidateMode, but that would be bound to submit, not to first vs. rest. I'll see if there's good ways to implement that sometime. In the meantime, this will work well enough to avoid duplicate errors (from having onBlur validators as well as onChange)
listeners: {
onBlur: ({ formApi }) => {
// The form hasn't been interacted with yet.
// note that this is not the same as RHF's !isDirty.
// See https://tanstack.com/form/latest/docs/framework/react/guides/basic-concepts#understanding-isdirty-in-different-libraries
if (formApi.state.isPristine) {
// trigger the change validator
formApi.validate('change')
}
}
}
listeners: {
onBlur: ({ formApi }) => {
// The form hasn't been interacted with yet.
// note that this is not the same as RHF's !isDirty.
// See https://tanstack.com/form/latest/docs/framework/react/guides/basic-concepts#understanding-isdirty-in-different-libraries
if (formApi.state.isPristine) {
// trigger the change validator
formApi.validate('change')
}
}
}
metropolitan-bronze
metropolitan-bronzeOP4mo ago
It works great! Thank you so much 🙏 And it would be great to expand revalidateLogic to stuff outside of first-submit, like in this use case.
exotic-emerald
exotic-emerald4mo ago
oh, oops. Yes, formApi.state.isPristine is the correct one I think the use case can be boiled down to:
validationLogic: multipleEvents(['change', 'blur']),
validators: {
onDynamic: // ...
}
validationLogic: multipleEvents(['change', 'blur']),
validators: {
onDynamic: // ...
}
or do you need to differentiate between events and this is the one edge case where it should be triggered?
metropolitan-bronze
metropolitan-bronzeOP4mo ago
A way to do onTouched is just the one use case that I needed. I personally don't need anything more custom than what tanstack-form already allows me to very easily. But I do think that the onTouched behavior is a common enough use case in the world form management that it would be great if it was documented on tanstack.com
adverse-sapphire
adverse-sapphire3w ago
I've been struggling over how to implement RHF's onTouched mode in tanstack form. @Luca | LeCarbonator 's suggestion gets quite close (if I've understood it correctly), but with the onChange still reporting errors before the first blur onChange. I think I've finally found a pattern that replicateds RHF's onTouched mode exactly:
validators: {
onChange: ({ fieldApi }) => {
if (!fieldApi.state.meta.isBlurred) {
// skip error reporting if the field has not been blurred before
return undefined;
}

// whatever validation logic here
}
},
listeners: {
onBlur: ({ fieldApi }) => {
// this makes it validate on blur but while still having errors change or get cleared onChange.
fieldApi.validate("change");
}
}
validators: {
onChange: ({ fieldApi }) => {
if (!fieldApi.state.meta.isBlurred) {
// skip error reporting if the field has not been blurred before
return undefined;
}

// whatever validation logic here
}
},
listeners: {
onBlur: ({ fieldApi }) => {
// this makes it validate on blur but while still having errors change or get cleared onChange.
fieldApi.validate("change");
}
}

Did you find this page helpful?