Currently, calling validateField('firstName') returns void (or Promise<void>. Calling validateForm(), on the other hand, returns the object with errors for the entire form.
validateField Returning a single key-value pair, i.e. { firstName: 'First name is required' }, if invalid, or undefined if the field is valid.
Adding a return value to the function call. Since it is returning Promise<void> right now, I imagine this would not be a breaking change.
Is there any reason in particular that it is set up this way?
Users triggering manual validation of fields. My use case is a custom "Wizard", and I want to validate a limited number of fields for each step, without validating fields on further steps. Something like this:
export const ContactInfo = () => {
const formik = useFormikContext()
const { validateField } = formik
const { goToStep } = useSequence()
const next = async () => {
const validated = await Promise.all([
validateField('firstName'),
validateField('lastName'),
validateField('emailAddress'),
validateField('phone'),
validateField('agreeToEmail'),
])
const invalidFields = validated.filter(v => v !== undefined)
if (invalidFields.length === 0) goToStep('nextStep')
}
return (
<div>
<Heading level={3}>Register for your visit</Heading>
<Field name="firstName" label="First Name" />
<Field name="lastName" label="Last Name" />
<Field name="emailAddress" label="Email" type="email" required />
<Field name="phone" label="Mobile Phone Number" type="tel" required />
<Field
name="agreeToEmail"
type="checkbox"
label="I agree to receive email of photos/video taken during the Event"
/>
<button onClick={next}>Continue</button>
</div>
)
}
I can use some state to work around this for now, but it's a little hacky. Here's the workaround:
import * as React from 'react'
import { useFormikContext } from 'formik'
import { Field } from '../../components/Forms'
import { useSequence } from '../../components/Sequence'
import { Heading } from '../../components/Text'
const { useState, useEffect } = React
interface ContactValues {
firstName: string
lastName: string
emailAddress: string
phone: string
agreeToEmail: boolean
}
export const ContactInfo = () => {
const { errors, setTouched, validateForm } = useFormikContext<ContactValues>()
const [shouldProceed, setShouldProceed] = useState(false)
const { goToStep } = useSequence()
const next = async () => {
setTouched({
firstName: true,
lastName: true,
emailAddress: true,
phone: true,
agreeToEmail: true,
})
await validateForm()
setShouldProceed(true)
}
useEffect(() => {
if (shouldProceed === false) return
if (
errors.firstName ||
errors.lastName ||
errors.emailAddress ||
errors.phone ||
errors.agreeToEmail
) {
setShouldProceed(false)
return
}
goToStep('nextStep')
}, [shouldProceed, errors])
return (
<div>
<Heading level={3}>Register for your visit</Heading>
<Field name="firstName" label="First Name" />
<Field name="lastName" label="Last Name" />
<Field name="emailAddress" label="Email" type="email" />
<Field name="phone" label="Mobile Phone Number" type="tel" />
<Field
name="agreeToEmail"
type="checkbox"
label="I agree to receive email of photos/video taken during the Event"
/>
<button type="button" onClick={next}>
Continue
</button>
</div>
)
}
@good-idea just wanted to thank you for a really well written bug report with an alternative I used to work around an almost identical issue!
This feature would be immensely useful.
validateField Returning a single key-value pair, i.e. { firstName: 'First name is required' }, if invalid, or undefined if the field is valid.
I'm curious why you think this should return an object rather than just a bare string of the error. It seems like that would be more intuitive, where validateField("some.field[5].value") returns the next value of errors.some.field[5].value
I was a bit surprised that this wasn't the behavior of the function already
This is something awesome to have actually. For the given scenario, any other solution would be counter-intuitive. Came across a similar situation where I badly need this feature.
+1 for the feature. This would be extremely helpful when creating custom onBlur functions where I submit or change global state onBlur but need to validate the input first. As it currently stands I have to validate all fields and only then I can change global state or submit that value. Again, this is helpful when I would like to submit data on a field input basis and not have a button click that submits all fields.
Most helpful comment
This feature would be immensely useful.
I'm curious why you think this should return an object rather than just a bare string of the error. It seems like that would be more intuitive, where
validateField("some.field[5].value")returns the next value oferrors.some.field[5].value