Formik: Auto touch on submit

Created on 19 Feb 2018  ·  29Comments  ·  Source: formium/formik

Bug, Feature, or Question?

Feature/Question

Current Behavior

I only show error messages when a field is touched and has a error message. The problem when clicking submit it should show error messages for all fields but since they are not getting marked as touched no error shows up

Desired Behavior

When emitting the handleSubmit event all fields in the form should be touched/there should be a flag that indicates if the form was submitted and not reseted.

  • Formik Version: 0.11.10
  • OS: Mac OS
  • Node Version: v9.5.0
  • Package Manager and version: Yarn 1.3.2

Most helpful comment

initialValues are required and should always be specified

All 29 comments

I agree. I just ran into this as well. I would categorize this as a bug unless there is some flag that needs to be set to make this happen.

When we are submitting, we want the invalid fields to display their errors, so they should automatically be touched.

Seems like even if I manually have called setFieldsTouched on the fields, then when handleSubmit is called it is clearing out the touched object so then the errors don't show.

It looks like the problem occurs when initialValues are not specified.

So when handleSubmit calls setNestedObjectValues with the values, they are {} and so does an Object.keys(values) and comes up with none.

So touched is returned as {}

When using yup schema it should get the fields to touch from the schema rather than the initialValues object.

I think solutions could be as follow:

  1. Use the error object keys to populate touched
  2. Use the submitted value object keys to populate touched
  3. Add a separate field to specify form fields (with API methods to dynamically add or remove fields)
  4. Add a flag that is true when the form is submitted and gets reseted to false when form gets reseted

All 4 solutions would fix this issue. The last one is probably the easiest to implement.

This behavior exists in formik. All fields are set to touched before submit.

@jaredpalmer like @jeffbski says it does not work without initializing the form with data.

initialValues are required and should always be specified

It cannot depend on schema because not al users use schema validation

@jaredpalmer when a developer does use validationSchema then obviously that can allow formik to know the fields, so if that is supplied it could be used by formik. No it is not required, but frankly I appreciate the value of yup and even the special optional support by formik, so it wouldn't be wrong to use the schema if it is provided.

formik + yup = fantastic

The fact that you provide this as an option gives it an advantage over the other competitors.

@jaredpalmer The documentation only states that initialValue should be used to prevent possible form state management issues (which in my case do not exist as I use custom forms). Based on what you state you should make clear in the documentation that initialValue is REQUIRED regardless as there is logic (like form submit) based on it. I still think any of my four suggestions would work as well without breaking the current version of the library but give additional flexibility to developers.

@jaredpalmer: initialValues are required and should always be specified

The README.md states that it's optional, is it a typo then?

I'm using the withFormik hoc, and there is no initialValues prop. I tried using mapPropsToValues but still it doesn't works as expected :/

Well, after using mapPropsToValues I got a quite different behavior:

  • If none of the fields were touched, clicking on submit do not shows any errors. _(which is wrong)_
  • If the fields were touched, but no values inserted, and error messages are already showing up, clicking on submit clears any errors _(which is wrong)_
  • If the fields were touched, values were inserted and cleaned up, and error messages are already showing up, clicking on submit do not clears any errors _(which is ok)_

Given that, it seems that the touched logic is quite misleading or wrong?

After digging into this one for a little bit I think the main reason this is an issue is because Formik doesn't know about the field to set as touched unless you set it on initialValues.

The only partial solution I can think of is to have <Field> (and not everyone uses field) tell Formik about it's self even though it has never set a value or have another mechanism in which to tell Formik about fields that haven't gone through setFieldValue yet.

I'm just doing initialValues={{ohWell: null }} and calling it a day for now.

Yeah this is super confusing. I spent a good while wondering why validation on submit wasn't working before finding this thread.

See #691 as well.

Any updates regarding this issue?

initialValues are required.

@jaredpalmer Thank you for the reply, after reading into it more, the behavior that I want given my situation can be achieved by checking the submitCount.
Have a nice day!

I've been using this to set my fields to touched

  const getTouchedObj = (errors: any) => {
    const touched: any = {}
    Object.keys(errors).map(key => {
      if (Array.isArray(errors[key])) {
        errors[key].map((val: any, index: any) => {
          if(index == 0)  touched[key] = [];
          touched[key].push(getTouchedObj(val));
        });
      } else {
        touched[key] = true;
      }
    });

    return touched;
  };

so in my submit I just call

setTouched(getTouchedObj(errors))

I'm pretty new to this so feedback or better ideas are very appreciated 👍

Fields that aren't in the initialValues (for exp dynamic fields) object won't be present in "touched" object, when you click submit the touched field will only contain fields that are present in initialValues, for example:
in the example below you will see the errors when submitting and if you check the touched object you will find {name: true, email: true}
` enableReinitialize
initialValues={{
name :"",
email: ""
}}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log('%c⧭', 'color: #731d6d', values);
}
}

`

but in this next example let's assume you will push fields into an array you don't know what fields will be in so the array is initially empty:
var myFields = [] <Formik enableReinitialize initialValues={{ fields: myFields }} validationSchema={validationSchema} onSubmit={(values) => { console.log('%c⧭', 'color: #731d6d', values); } } >

after pushing fields for example name and email into myFields and click submit the fields won't be present in initial values and nor in touched that's why you won't see errors.
in that case you should use errors object

This behavior exists in formik. All fields are set to touched before submit.

Is there any way I can overwrite this behavior ... I have a case where I must validate only fields that are actually touched on submitting ... NOTE:- I am using yup validation object

I've been using this to set my fields to touched

  const getTouchedObj = (errors: any) => {
    const touched: any = {}
    Object.keys(errors).map(key => {
      if (Array.isArray(errors[key])) {
        errors[key].map((val: any, index: any) => {
          if(index == 0)  touched[key] = [];
          touched[key].push(getTouchedObj(val));
        });
      } else {
        touched[key] = true;
      }
    });

    return touched;
  };

so in my submit I just call

setTouched(getTouchedObj(errors))

I'm pretty new to this so feedback or better ideas are very appreciated 👍

So, where you get error object? As I understand you call setTouched into function onSubmit, but I don't know where get this?

@DinAlla
If I remember correctly the errors are available inside the component like this:

<Formik
 initialValues={yourInitialValues}
onSubmit={yourHandleSubmitMethod}
>
    {({ errors, ...andLotsOfOtherStuff }) => (
          //form markup goes here
       )
    }
</Formik>


Hope this helps!

@DinAlla I guess what you perhaps can do is save the error in state, and then use it in that handler (haven't tested it but this should be the gist):

const getTouchedObj = (errors) => { ... }

const MyForm = () => {
  const [validationErrors, setValidationErrors] = useState();

  return (
    <Formik
      onSubmit={(values, formikBag) => {
        if (validationErrors) {
          formikBag.setTouched(getTouchedObj(validationErrors));
        }
        handleSubmit(values);
      }}
     >
     {(formik) => {
       const { errors } = formik;
       // set the errors here
       setValidationErrors(errors);
     }
   );
};
  >
)

But this is getting messy...

I'm using the withFormik hoc, and there is no initialValues prop. I tried using mapPropsToValues but still it doesn't works as expected :/

I am having the same issue as well. Anyone managed to get it to work while using withFormik hoc?

I just hit this 'issue' as well. To be honest, making initial values mandatory is a bit error-prone. You can forget to do it and no error is thrown to indicate your fault.

I have written my own validation library (without knowing Formik exists) and I actually used the same principles.

I have also hit the same problem - I did not know which fields the form has during onSubmit validation. I have solved it by using 'proprietary' error field all. Then eg. the field firstName is touched when touched.firstName || touched.all instead of just touched.firstName. You set touched.all to true on submit, making all the fields "touched" without knowing their names.

Next the all flag should be stored outside the error object to allow usage of all field if it exists (I did it for simplicity).

Here's a patch that seems to achieve it:

import flatten from 'flat';
const FormikPatchTouched = () => {
  const { errors, setFieldTouched, isSubmitting, isValidating } = useFormikContext();
  useEffect(() => {
    if (isSubmitting && !isValidating) {
      for (const path of Object.keys(flatten(errors))) {
        setFieldTouched(path, true, false);
      }
    }
  }, [errors, isSubmitting, isValidating, setFieldTouched]);
  return null;
};
<Formik>
  {() => {
    return (
      <Form>
        <FormikPatchTouched />
        {/* ... */}
      </Form>
    )
  }}
</Formik>

@konrad-garus Your solution seems to work for me also

Was this page helpful?
0 / 5 - 0 ratings

Related issues

green-pickle picture green-pickle  ·  3Comments

najisawas picture najisawas  ·  3Comments

PeerHartmann picture PeerHartmann  ·  3Comments

dearcodes picture dearcodes  ·  3Comments

jaredpalmer picture jaredpalmer  ·  3Comments