Search code examples
reactjsformik

Show errors both (1) on Submit click and (2) individually on blur/change with 'touched'


In Formik, I need validation errors to be displayed in both cases:

  1. On Change/Blur, individually as fields get touched on the form -- not all at once, during the filling-out process
  2. On clicking Submit at any time: here, all errors should be shown at once.

(1) is working but (2) is not. When I come to the form and just click the Submit button, nothing happens and the errors aren't displayed. The errors only get displayed when I touch the controls.

I think the problem is that to satisfy (1), my controls have

isInvalid={touched.startDate && errors.startDate}  

But when I click Submit immediately, the control hasn't been touched yet, so this isInvalid condition fails. But I can't remove the touched.startDate part because otherwise, all invalid controls always start showing when filling out the form -- even the ones I haven't been touched. I need to keep my touched requirement when filling out, but also show all errors on Submit. The Submit is the one case where all errors have to be shown at once. Is it possible to pass some submitClicked variable somewhere to achieve this?

<Formik enableReinitialize 
        validationSchema={schema}
        onSubmit={ (values, { validate }) => {
            alert(JSON.stringify(values, null, 2));
        }}
        initialValues={{}}
>
   {({
      handleSubmit,
      handleChange,
      handleBlur,
      values,
      touched,
      isValid,
      errors,
     }) => (
         <Form onSubmit={handleSubmit}> 
             ...
             {/* EXAMPLE */}
             <Form.Control type="date"
                           name="expirationDate"
                           value={values.expirationDate}
                           onChange={handleChange}
                           onBlur={handleBlur}
                           isInvalid={touched.expirationDate && errors.expirationDate}    
             </Form.Control>
         </Form>

...
// Yup Schema used for form validation
const schema = yup.object().shape({
    expirationDate: yup.date().required('Expiration Date is required'),
    frequencyDays: yup.number().required('Frequency is required'),
    interval: yup.string().required('Frequency interval is required'),
    ...           

Solution

  • Since a lot of people are viewing this thread, here's what we've ended up with. This is our React Bootstrap control example, in this case a Select called frequencyDays inside a Formik. Note the following:

    isInvalid={(submitClicked && errors.frequencyDays) || 
               (!submitClicked && touched.frequencyDays && errors.frequencyDays)}
    

    This means that either (1) Submit was clicked and errors exist, or (2) Submit was NOT clicked, but this control was touched, and errors exist.

    <Form.Control as="select"
        id="dropdownFrequencyDays"
        name="frequencyDays"
        value={values.frequencyDays}
        onChange={handleChange}
        onBlur={handleBlur}
        isInvalid={(submitClicked && errors.frequencyDays) || (!submitClicked && touched.frequencyDays && errors.frequencyDays)}
    

    SubmitClicked is a variable, initialized/set as follows:

    // Shows whether the Submit button was clicked (used to show all form validation errors at once)
    // initially FALSE
    const [submitClicked, setSubmitClicked] = useState(false);
    

    set in Submit Button's onClick:

    <Button type="submit" disabled={isSubmitting} 
       onClick={() => { 
           setSubmitClicked(true); // We set it to TRUE in Submit's onClick
                }} 
       variant="primary">Submit</Button>
    

    If you're using Material UI, it's similar to React-Bootstrap and it has some error property on its controls which is analogous to isInvalid.