Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate only one field at a time #512

Open
orenklein opened this issue Mar 13, 2018 · 129 comments
Open

Validate only one field at a time #512

orenklein opened this issue Mar 13, 2018 · 129 comments

Comments

@orenklein
Copy link

Bug, Feature, or Question?

Feature/Question

Current Behavior

Currently, validation is running asynchronously on the whole schema (assuming you use ValidationSchema of yup). It is reasonable to run the validation on submit, however, It is extremely cumbersome to run it on every field change/blur.

Especially if you have some backend validation (using yup.test).
Assuming you have a couple of fields, field A, and field B - the later must go through backend validation. Every time you change field A, the validation runs both on A and B which will cause unnecessary backend calls.

I also tried using the new alpha version's handleChange('fieldName') but I still experience the same behavior.

Suggested Solutions

  1. Using yup schema, using yup.reach seems reasonable, even though I'm not sure how is its performance ([Discussion] Whole form validation #35)

  2. Formik validate function - pass the event's field name. It will allow the developer to validate only one field at a time.

Environment

  • Formik Version: 0.11.11 /1.0.0-alpha.2
  • React Version: 16.2
  • TypeScript Version: 2.7.2
  • CodeSandbox Link:
  • OS: macOS High Sierra
  • Node Version: 8.9.4
  • Package Manager and Version:
@jaredpalmer
Copy link
Owner

We could prototype this with a special version of Field. The tougher bit is how to do it without Yup.

Open to PRs. Also FYI/reference, unlike Field, FastField tries to run validation synchronously before running it async. Perhaps that technique should be moved as the default

@jquense
Copy link
Contributor

jquense commented Apr 11, 2018

For the general case, it seems like just passing fieldName to validate is "good enough" to let someone handle that if they want. You can implement validationSchema in terms of that api for single field validations. It's not particularly hard to do with yup, react-formal implements it like this: https://github.com/jquense/react-formal/blob/master/src/Form.js#L368-L383

@jaredpalmer
Copy link
Owner

Interesting. Will check this out.

@JonathanStoye
Copy link

JonathanStoye commented Jun 21, 2018

Any updates on this? Otherwise I would give @jquense idea of passing the fieldName to validate a shot if you don't mind.

EDIT:
Nevermind. Since we are only doing client side validation before the submit, touched.email && errors.email is totally working for me. So I am only displaying the errors when the fields have been touched. Of course that is not solving the issue with server side validation. Sorry for making noise here.

@sshmyg
Copy link

sshmyg commented Jun 28, 2018

Any solutions?

@sshmyg
Copy link

sshmyg commented Jun 28, 2018

test method of yup schema invokes even change some other field. It's very inconvenient.

@jaredpalmer
Copy link
Owner

jaredpalmer commented Jun 28, 2018 via email

@sshmyg
Copy link

sshmyg commented Jun 28, 2018

@jaredpalmer Can you provide more details, please?
I don't use Field component from formik.

@sshmyg
Copy link

sshmyg commented Jun 28, 2018

Oh, you mean I don't need .test in yup schema and just validate in every require field separately?

@sshmyg
Copy link

sshmyg commented Jun 28, 2018

Validate all schema onChange, it's very bad solution.

@jaredpalmer
Copy link
Owner

See 1.0.0-beta.3 Field validate.

@sshmyg
Copy link

sshmyg commented Jun 29, 2018

I think it's not an option use some Field component.
Question was about yup.test validation.
You should warn in readme than about using yup.
test and 'transform' doesn't work as expected.

@jaredpalmer
Copy link
Owner

feel free to submit a PR.

You can always use the normal validate function instead of validationSchema and then use Yup to to get the functionality you need.

@suberg
Copy link

suberg commented Aug 20, 2018

@jaredpalmer if I pass validate function to my Field components, it runs all these functions when I edit only one field, ON EVERY SINGLE KEYSTROKE. Why? It's not obvious. I don't want to use Yup. Is there a possibility to disable this behaviour and run one validate function?

@idesignpixels
Copy link

Would also like a solution to this, my scenario we query a pay as you go api for validation and this would call it over and over with the same value unless we intervene.

@sshmyg
Copy link

sshmyg commented Aug 30, 2018

@idesignpixels Same problem, check user or some other API call will lead to error 503 (Service Unavailable)

@jaredpalmer
Copy link
Owner

@suberg @idesignpixels let me make sure I am understanding the suggested behavior:

  • Formik validationSchema
    • Runs only validation for each field's key onChange/onBlur or equivalent imperative methods.
    • Runs all as full schema prior to submit
  • Formik validate
    • Stays the same as this is offloaded to userland
  • Field validate
    • Stays same, single value is first argument

@ivanquirino
Copy link

@jaredpalmer let's say we have username and email fields. We set our validation schema to

yup.object().shape({
  email: yup.string().required().email()
}}

Then we pass a custom validation function as validate prop to the username Field that validates if our username is unique. What is happening is that when I type at the email field, the validate function provided to the username field is also running.

@stale
Copy link

stale bot commented Dec 3, 2018

Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal.

@stale stale bot added the stale label Dec 3, 2018
@stale
Copy link

stale bot commented Dec 10, 2018

ProBot automatically closed this due to inactivity. Holler if this is a mistake, and we'll re-open it.

@stale stale bot closed this as completed Dec 10, 2018
@sshmyg sshmyg mentioned this issue Dec 10, 2018
24 tasks
@jaredpalmer jaredpalmer reopened this Dec 10, 2018
@kafeelkhatri
Copy link

So onething what i found out is that using touched feature that will validate on handleSubmit and after that it will work perfectly by validating every field
{({ handleChange, handleSubmit, errors, values, isValid, touched,})

Give it a try maybe it will help

@ldwrs
Copy link

ldwrs commented Oct 29, 2022

Ideally I’d have a yup schema and only the field that’s been changed gets validated… Maybe this isn’t the best place to ask, but could React Hook Form do this?

@npearson72
Copy link

@ldwrs I'm currently running into this issue with react hook form. Haven't found a solution yet.

@patrickyan
Copy link

@ldwrs Can confirm that React Hook Form runs the test on every keystroke as well

@wallace-sf
Copy link

I'm currently creating a form step and before to switch page i want to validate page fields. So, i want validate one field at time... Formik does not provide a simple way to do that.

@cigzigwon
Copy link

cigzigwon commented Jan 26, 2023

When dealing with Formik customization that's specialized we don't use Yup and we disable validateOnBlour valdateOnChange

And use a functional pipeline w/reducer:

export const validate = (values, tabs, index, uppy) => {
  const errors = Object.entries(values).filter(([key, value]) => (
  	key.match(tabs[index]) !== null // match tabname
  ))
	.map(([key, value]) => {
  	let error = {}
  	const elem = document.getElementById(key)
  	const select = elem ? M.FormSelect.getInstance(elem) : null
  	const validator = validators[key] ? validators[key] : null
  	// run validation per field key
  	if (validator) {
  		error[key] = validator(value)
  	}
  	// change class on M.Select (menu)
		if (error[key] && select) {
			select.wrapper.className = select.wrapper.className + ' invalid'
		} else if (select) {
			select.wrapper.className = 'select-wrapper'
		}
		return error
	})
	.reduce((acc, error) => {
  	const key = Object.keys(error)[0]
  	if (!error[key]) {
  		return acc
  	}
  	acc[key] = error[key] 
  	return acc
  }, {})

	if (Object.keys(errors).length) {
		console.log('Formik errors', errors)
		scroll_reset()
	}

	if (index + 1 >= tabs.length) {
		const valid = valid_files(uppy)
		if (valid !== undefined) {
			return {...errors, ...{ docs_checklist: valid }}
		}
	}
  
  return errors
}

@cigzigwon
Copy link

@wallace-sf So much faster this way. You just need to call the validators you define per trigger event that you override and then you can replace Yup w/your own functionaly reducer. My form has six tabs and varying workflows. On submit fires on every tab change foraward or backawards. It does everything and also has an Uppy hook in the form. Sometimes you can cook up your own function that's thrity lines of code that does exactly what you need to do.

@cigzigwon
Copy link

validators.js (my code is very modular)

export const is_one = value => (
	!value ? "Choose at least one" : undefined
)
export const is_required = value => (
	!value ? "Field is required" : undefined
)
export const multi_one = values => (
	!values.filter(value => (
		value !== ""
	)).length ? "Choose at least one" : undefined
)
export const min_length = (value, length) => (
	value.length < length ? `Minimum of ${length} characters in length` : undefined
)

@ldwrs
Copy link

ldwrs commented Feb 1, 2023

@cigzigwon I don't think this is possible when your validation rules depend on other field values?

@cigzigwon
Copy link

@ldwrs No you are absolutely incorrect. Use ingenuity.

@ldwrs
Copy link

ldwrs commented Feb 1, 2023

I think you're missing the point. You can validate the whole form on submit of course. But what we need is validating one field only onchange (immediately) with validation rules that depends on other field values.

@cigzigwon
Copy link

cigzigwon commented Feb 2, 2023

@ldwrs My working example is too complex to share. My point is that if you have skills put them to use. If not then, I guess you are stuck aren't you? I'm not here to outline every single case nor have time. Formik can do anything you just have to utilzie it on a case per case basis. I'm having no trouble with this issue. My stuff all runs in production with all of these cases solved by not using Yup.

@ldwrs
Copy link

ldwrs commented Feb 3, 2023

Ok, I see you can not give an example of what is asked. I think anyone can draw the correct conclusions from this simple fact.

@Zach-Jaensch
Copy link

Zach-Jaensch commented Feb 16, 2023

@ldwrs No you are absolutely incorrect. Use ingenuity.

Your response is incredibly rude, and if you are not able to provide a simplified example, I could argue that you don't have the skills needed.

@cigzigwon I don't think this is possible when your validation rules depend on other field values?

We don't use yup in our project (sadly not my choice) but we have been able to validate fields on an individual level, even when they are coupled to other fields. I can imagine a similar concept might be capable with yup, but you may have to change how you implement it.

(Note: parts unrelated to validation are removed)
We created a "wrapping" component so we can compose any input type with it

function FormikField(props) {
  const Component: any = props.component || BasicInput;

  const { values } = useFormikContext();

  function handleValidation(value) {
    if (required && !value) {
      return "Required"
    }

    if (validation) {
      return await validation(value, values) || undefined;
    }

    return undefined;
  }

  const [field] = useField({
    name: props.name,
    validate: handleValidation
  });

  return <Component {...field} />
}

One of our more simple inputs

            <FormikField
              name="email"
              label="Email
              type="email"
              autoComplete="username"
              required
              validation={(value) => /* email matchs regex */ ? undefined : "Incorrect Format"}
            />

@cigzigwon
Copy link

cigzigwon commented Feb 16, 2023

I'm simply not doing your thinking and work. If you guys knew how to combine functional pipelines with a decorator pattern. This wouldn't be an issue for you today. I've migrated everything into Phoenix LiveView by now so Formik is now obsolescent in our apps since December 25th. There's nothing you could say to make me think less of myself. I'm just amused.

@Zach-Jaensch
Copy link

This is a community page centred around formik and the issue clearly many people have. Please stick to the subject of supporting the community and not using it as a forum to demonstrate your ego. To help build a community (and a team) of great engineers, the knowledgable people should always be willing to help, not hinder and put down as you seam to be attempting.

@cigzigwon
Copy link

cigzigwon commented Feb 16, 2023

My original counter argument is that it's not really an issue if you just start writing code. At some point you stop copy/pasting the work of a great engineers

Besides Do you really think I care what you think?

@cigzigwon
Copy link

My best advice for this lib as working functionally is that the common structure of errors be changed to an array of objects with support for a functional return. And that Yup be not used. And the moving everything so that an array of objects that have functional returns as validators. The fields that depend on fields are one of but can b dealt with in the functional return pattern as they can reach into another validator. Forms are lists of lists. So array of object patterns would have been ideal. The key to get what you need from Formik is flexibility and congruency between lists of lists. A common structure in a functional language which JavaScript is. I could probably just take this summation to make a simple port for LiveView. Kinda already has this out of the box though. I'm unsubscribing as going any deeper is a waste of my time

@meysamzi
Copy link

How can i have a real-time validation with Formik?
i mean when a user types in an input field not when he/she clicks on Submit button or when onBlur happens!

@kjschulz
Copy link

Better late than never 😂??? Thanks to inspiration from @pedroabreu and @cigzigwon, I was able to get this working with a normal Formik/React Bootstrap (BS v5) install with a validationSchema that utilizes yup without too much customization...in case this helps anyone else. This will allow each field to be validated, one at a time, as changes are made and also still performs the default whole form validation on each form submit:

<Formik
  initialValues={myInitialValues}
  validationSchema={myYupSchema}
  validateOnChange={false} // Disable validation every field change
  validateOnBlur={false} // Disable validation every field blur
>
  {({
    handleSubmit,
    handleChange,
    handleBlur,
    validateField,
    values,
    touched,
    isValid,
    errors
  }) => {
    // Avoid a race condition to allow each field to be validated on change
    const handleInputChange = async (e, fieldName) => {
      await handleChange(e);
      validateField(fieldName);
    };

    return (
      <Form noValidate onSubmit={handleSubmit}>
        <Row className="mb-3">
          <Form.Group as={Col} md="6" controlId="validateFirstName">
            <Form.Label>First Name</Form.Label>
            <Form.Control
              type="text"
              name="firstName"
              value={values.firstName}
              onChange={(e) => handleInputChange(e, 'firstName')}
              isValid={touched.firstName && !errors.firstName}
              isInvalid={!!errors.firstName}
            />
              ...
      </Form>
    )
  }}
</Formik>

@ml054
Copy link

ml054 commented Mar 22, 2023

Not sure if you know but formik is dead for years, so maybe it is time for change? ;-) New season new form library. Welcome in JS world. :)

@kjschulz
Copy link

kjschulz commented Mar 22, 2023

Hmm, ok thanks @ml054. I noticed that the project is not currently being maintained but I guess I didn't realize it was dead. It's still listed in the current documentation as the main suggested option in React-Bootstrap's latest version and on many React form validation library round ups from just last year.

Out of curiosity though, what would you recommend?

@ml054
Copy link

ml054 commented Mar 23, 2023

I'd say https://npmtrends.com/ to avoid doing advertisement. ;-)

@wallace-sf
Copy link

@wallace-sf So much faster this way. You just need to call the validators you define per trigger event that you override and then you can replace Yup w/your own functionaly reducer. My form has six tabs and varying workflows. On submit fires on every tab change foraward or backawards. It does everything and also has an Uppy hook in the form. Sometimes you can cook up your own function that's thrity lines of code that does exactly what you need to do.

@cigzigwon That's right. I made a first version, as you said. Later, for other page steps, I decided to use a Formik component for each page. No parent Formik component for pages. It was easier that way.

@cigzigwon
Copy link

cigzigwon commented Apr 6, 2023

@wallace-sf Yes, all it really is a primary validation function (decorator) for onSubmit actions and then for "one field validation" simply apply it to that field or inject it into custom Component. We should alsways use hooks in this lib and avoid Yup. When the use cases evolve beyond smaller digital ,marketing forms. Yup would also work fine in most back-end data entry forms where the entmry is more straightforward and less dependant on other field requirements. I do appreciate you for leaving a comment today. At one point some wanted more examples but I do not have time to provide them but this is my last comment in this thread. Thanks!

@cigzigwon
Copy link

@kjschulz I agree that's the defacto way to overide the underlying Promises with async/await in the handler but some might be missing that point here. I spent a while on it myself.

@Ismail0v12
Copy link

Ismail0v12 commented May 2, 2023

Hello everone, here i am coming with solution, in formik has function validateField(value: string), so if you pass this function to props onBlur or onChange, it is validating only passed field to validateField, like this:
const handleValidation = useCallback( (fieldName: string) => ({ onBlur: (e: React.FocusEvent<HTMLInputElement, HTMLInputElement>) => { registerForm.validateField(fieldName); }, onChange: (e) => { registerForm.handleChange(e); if (registerForm.touched && registerForm.errors[fieldName]) { registerForm.validateField(fieldName); } }, }), [registerForm] );
You can this use this solution without using Fomik parent component!
if you have better solution with validateField function just let us know!
Happy coding :)

@NicoConstantin
Copy link

Better late than never 😂??? Thanks to inspiration from @pedroabreu and @cigzigwon, I was able to get this working with a normal Formik/React Bootstrap (BS v5) install with a validationSchema that utilizes yup without too much customization...in case this helps anyone else. This will allow each field to be validated, one at a time, as changes are made and also still performs the default whole form validation on each form submit:

<Formik
  initialValues={myInitialValues}
  validationSchema={myYupSchema}
  validateOnChange={false} // Disable validation every field change
  validateOnBlur={false} // Disable validation every field blur
>
  {({
    handleSubmit,
    handleChange,
    handleBlur,
    validateField,
    values,
    touched,
    isValid,
    errors
  }) => {
    // Avoid a race condition to allow each field to be validated on change
    const handleInputChange = async (e, fieldName) => {
      await handleChange(e);
      validateField(fieldName);
    };

    return (
      <Form noValidate onSubmit={handleSubmit}>
        <Row className="mb-3">
          <Form.Group as={Col} md="6" controlId="validateFirstName">
            <Form.Label>First Name</Form.Label>
            <Form.Control
              type="text"
              name="firstName"
              value={values.firstName}
              onChange={(e) => handleInputChange(e, 'firstName')}
              isValid={touched.firstName && !errors.firstName}
              isInvalid={!!errors.firstName}
            />
              ...
      </Form>
    )
  }}
</Formik>

This works also with onBlur event, if you have your form modularized you can access to handleChange or handleBlur and also to validateField from form parameter:
Example:

{({ field, form }) => (
<input
className={inputClass(errors[data.name])}
type={data.type}
{...field}
onBlur={async e => {
await form.handleBlur(e)
form.validateField(data.name)
}}
/>
)}

@getsnoopy
Copy link

getsnoopy commented Nov 1, 2023

I don't understand why this behaviour isn't considered a bug; I just had to go down a debugging rabbit hole and try all sorts of things to make sure I wasn't calling the same validator function multiple times or the like to see why my validator functions for other fields were running when an unrelated field changed. The validation documentation clearly says:

Form-level validation is useful because you have complete access to all of your form's values and props whenever the function runs, so you can validate dependent fields at the same time.

This implies that non-form-level validation (i.e., field-level validation) runs at the individual field level, as the name implies, and isn't intended for dependent field validation. So why is that being used as a reason for this behaviour? It seems like dependent fields are in the minority of cases rather than the majority, yet this silly behaviour is leading to the majority having to implement all sorts of gnarly workarounds.

The irony, of course, is that I initially was playing around with my setup and used the validate function at the form-level, and noticed that it was calling the onchange event for all the fields, which is not what I wanted. So, I went through the effort of changing all my code so that a different function would be passed into each field in an effort to get field level validation. But wait! It turns out it's the same thing anyway, and I just wasted an hour or more doing what I did.

Having to disable default validation/event handling functionality, reroute those calls to a custom event handler function that then manually calls the handleChange function and then calls the individual field validator function entirely defeats the purpose of the purported "composability" and avoidance of "prop-drilling" that Formik apparently provides according to its headline tutorial page.

The solution seems clear to me: just disable this behaviour. If people want dependent field validation, they can either do that at the form validation level, or we can implement some options called validateAllFieldsOnChange, validateAllFieldsOnBlur, etc. or something to that effect that enables the current behaviour for them.

@amidulanjana
Copy link

I had the same issue; it runs the test method on every keystroke. So, I went with useRef. It prevents the unnecessary running of the test method when other fields change.

.test('domain-server-validation', 'This domain is already in use', async domain => {
    if (domain && domainFieldValidationRef.current !== domain) {
      domainFieldValidationRef.current = domain;
      .....
      return false
    }
    return true
 })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests