formik
formik copied to clipboard
return value for validateField
🚀 Feature request
Current Behavior
Currently, calling validateField('firstName')
returns void
(or Promise<void>
. Calling validateForm()
, on the other hand, returns the object with errors for the entire form.
Desired Behavior
validateField
Returning a single key-value pair, i.e. { firstName: 'First name is required' }
, if invalid, or undefined
if the field is valid.
Suggested Solution
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?
Who does this impact? Who is this for?
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>
)
}
Describe alternatives you've considered
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.
+1 for this feature.
My situation is like validating some fields in one of the sections of the form before going to next section,
After running for loop validateField()
of those fields, the formik instance is not updated with the latest errors,
so no errors caught and thus passed to next section and missed some mandatory fields in the previous section.
+1 for this feature. Facing the same situation as @jagwingchoy
+1 for this feature. My situation is like validating some fields in one of the sections of the form before going to next section, After running for loop
validateField()
of those fields, the formik instance is not updated with the latest errors, so no errors caught and thus passed to next section and missed some mandatory fields in the previous section.
@johnrom Sorry to quote you here as this discussion is not being aware. I do hope there is some solution or suggestion to handle the situation, thanks.
This is something I ran into when trying to validate a "resend confirmation email" button. There were two fields, email and code, and I wanted to validate both of them on form submit, but only validate email when clicking the resend button. Since validateField()
doesn't have a proper callback and the errors object doesn't update, I ended up using setFieldTouched
:
<button type="button" disabled={isSubmitting} onClick={() => {
setFieldTouched('email', true, true).then(err => {
if (!err.hasOwnProperty("email")) {
handleResend(values);
}
});
}}>Re-send confirmation code</button>
+1 for this feature.
+1 for this feature
+1 for this feature
+1 for this feature
+1 this would be helpful
+1 for this feature
+1 this would be very helpful
This is something I ran into when trying to validate a "resend confirmation email" button. There were two fields, email and code, and I wanted to validate both of them on form submit, but only validate email when clicking the resend button. Since
validateField()
doesn't have a proper callback and the errors object doesn't update, I ended up usingsetFieldTouched
:<button type="button" disabled={isSubmitting} onClick={() => { setFieldTouched('email', true, true).then(err => { if (!err.hasOwnProperty("email")) { handleResend(values); } }); }}>Re-send confirmation code</button>
this is a good workaround for me.
another thing is, setFieldTouched('email', true, true) triggered validation for the whole form. so if there is another input field for confirmation code, its error state will become true. I had to do error={formik.touched.code && Boolean(formik.errors.code)}
to prevent premature showing the error
+1 for this feature.
+1