Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Don't Validate untouched fields in Formik and Yup on submitting if field is not empty

I have this validation schema for a form made using withFormik() used in my React application, Here validateJql() is my custom validation function for yup

validationSchema: Yup.object().shape({
            rework: Yup.string().required("Rework query is required").validateJql(), 
            originalEstimate: Yup.string().required("Original Estimate query is required").validateJql()
        })

and my form Component is like this:

const addSomeForm = (props) => {
    const {
        values,
        touched,
        errors,
        isSubmitting,
        handleChange,
        handleSubmit,
    } = props;

return (
        <form onSubmit={handleSubmit}>
             <div className="form-group">
                  <div>
                      <label htmlFor="name" className="col-form-label"><b>Rework Query:</b></label>
                      <textarea id="query.rework" rows="5" type="text" className="form-control" placeholder="Enter JQL with aggregate Function" value={values.query.rework} onChange={handleChange} required />
                       {errors.query && errors.query.rework && touched.query && <span className="alert label"> <strong>{errors.query.rework}</strong></span>}
                   </div>
             </div>
             <div className="form-group">
                 <div>
                      <label htmlFor="name" className="col-form-label"><b>Original Estimate:</b></label>
                       <textarea id="query.originalEstimate" rows="5" type="text" className="form-control" placeholder="Enter JQL with aggregate Function" value={values.query.originalEstimate} onChange={handleChange} required />
                       {errors.query && errors.query.originalEstimate && touched.query && <span className="alert label"> <strong>{errors.query.originalEstimate}</strong></span>}
                 </div>
             </div>
       </form>
    )

Now, what I want to do is not to run validation on form submit if the field rework and originalEstimate is not touched and also not empty. How can I achieve this with withFormik HOC or Yup? I have partially been through Yup docs and Formik docs but could not find something to fit with my problem.

This is the case after submitting the form once and editing after that for minor tweaks in some of those multiple fields. if there are multiple fields and only some are edited, I don't want to run validation for all the fields existed.

Thank you in advance.

like image 326
ShivaGaire Avatar asked Feb 01 '19 05:02

ShivaGaire


2 Answers

I had a similar issue, I ended up creating another field where I set the value when showing the edit screen. Then i compare inside a test function like this :

originalField: yup.string().default(''),

field: yup.string().default('').required('Field is required.').test('is-test',
    'This is my test.',
    async (value, $field) => {
      if($field.parent.originalField !== '' && value === $field.parent.originalField) return true
      return await complexAsyncValidation(value)
    }

Not perfect, but definitely working

like image 21
Edwin Joassart Avatar answered Sep 18 '22 11:09

Edwin Joassart


This is the default desired behavior as stated in formik docs but i think you can do the following:

Instead of using validationSchema, use validate function.

Validate function will work the same way your validationSchema works. You just need to use Yup programmatically from a function with mixed.validate

So you can have the full control of all the props in your form. You could also use the getFieldMeta to get the touched and value of the field and use that in your validation. Or get those props from touched object in form with getIn

Something like:


// Some util functions
function mapYupErrorsToFormikErrors(err: { inner: any[] }) {
  return err.inner
    .filter((i: { path: any }) => !!i.path)
    .reduce(
      (curr: any, next: { path: any; errors: any[] }) => ({
        ...curr,
        [next.path]: next.errors[0],
      }),
      {},
    )
}

function validateSchema(values: object, schema: Schema<object>) {
  return schema
    .validate(values, {
      abortEarly: false,
      strict: false,
    })
    .then(() => {
      return {}
    })
    .catch(mapYupErrorsToFormikErrors)
}


// Your validation function, as you are using `withFormik` you will have the props present

function validateFoo(values, props) {
  const { touched, value } = props.getFieldMeta('fooFieldName') // (or props.form.getFieldmeta, not sure)

  const errors = validateSchema(values, yourYupSchema)

  if (!touched && !value && errors.fooFieldName) {
    delete errors.fooFieldName
  } 

  return errors  
}

Well, touched might not work for your use case because formik probably would set it to true on submission, but there you have all the props and you can use something different, like the empty value or some other state prop you manually set. You got all the control there.

like image 152
Diego Segura Avatar answered Sep 20 '22 11:09

Diego Segura