redux-form: handleSubmit ignores validation errors on unmounted fields

Hello,

I’ve just noticed that if I return an error from my validation function for a field which is not currently mounted, the error is ignored and the form is allowed to submit anyway.

The following code snippet isn’t a great use case example but it proves the behaviour:

@reduxForm({
    form: 'simple',
    validate:   (values) => ({
        firstName:  !values.get('firstName') ? 'required' : undefined,
        lastName:   !values.get('lastName') ? 'required' : undefined,
    }),
    onSubmit:   () => alert('submitting')
})
class SimpleForm extends React.Component {

    render() {
        return  <div>
                    <Field name="firstName" component="input" type="text" />
                    <button type="submit" onClick={this.props.handleSubmit}>Submit</button>
                </div>
    }
}

This form should actually never submit as there is never a value for lastName. But it seems that because lastName doesn’t exist as a <Field> component, its errors are ignored and the form is allowed to submit as long as firstName is filled in. Add a lastName field and the issue goes away.

I would expect any error I return to prevent form submission. An error is an error. The <Field> components I have mounted are display logic only, the data in the store is the source of truth.

My particular use case is that I have a form composed of multiple tabs. With this behaviour, validation is effectively run only for the fields which are on the currently active tab.

Another use case would be hidden fields. I consider < input type=“hidden” > to be obsolete now that I’m using redux. My hidden fields are in the store so there’s no need to have them in the DOM.

Any thoughts? Thanks

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 7
  • Comments: 23 (6 by maintainers)

Most upvoted comments

Hmmm… that seems like a complex and arbitrary solution to a single problem.

In my eyes, the form is either valid or it is not valid. The rest is an implementation detail that should be left to the programmer. A wizard should be accomplished either by using multiple forms or by writing my own logic for partial validation.

Wouldn’t this work?

validate = (values, props) => {
    if (props.step ==  1) {
        return validateStep1(values)
    } else if (props.step ==  2) {
        return validateStep2(values)
    } else if (props.step == 3) {
        return validateStep3(values)
    }
}

onSubmit should behave in one of two ways upon a submission attempt:

  1. Always run regardless of validation, leave it up to the programmer to figure out the rest
  2. Run only if no validation errors were returned

Anything in between is unpredictable. The behaviour “Do this if all fields currently in the DOM are valid” is unpredictable, opinionated and wierd.

@lustoykov I guess I over-complicated my response as I had created my own decorator to generalize it for all my forms and to accomplish some additional tasks as well.

When it comes down to it though, redux-form includes your validate function as a prop to your component, so there’s nothing stopping you from calling the validate function yourself before actually submitting your data.

onSubmit = values => {
    return new Promise( (resolve, reject) => { 				
	const errors = this.props.validate ? this.props.validate(values, this.props) : {};
	
	if (!errors) {
		try {
			var submitResult = postValues(values);
		} catch(e) {
			return reject(e);
		}
	} else {
		return reject(new SubmissionError(errors));
	}
    }
}

I don’t remember all the details as I go through my code, but I think you’ll want to return a promise and reject it if there are errors, otherwise redux-form will see the submission as successful.

Also, I’ve simplified the above code by just having if (!errors) but you may have to do some processing of the errors to detect yes or no depending on what you actually return from your validate function.

I think the better solution is to allow validation on all fields rather than not unmounting fields. There’s a couple reasons for this:

  1. All the fields may never have been mounted, so it still won’t solve the issue of validating all fields.
  2. The issue you’re trying to solve is that all fields must be validated. That’s only peripherally related to whether or not they’re mounted.

The direct approach is to add a prop like validateMountedFieldsOnly (defaults to true) which will switch between validating all fields and validating only mounted fields.

It seems like a reasonable change to me.

This is by design. Imagine you have a wizard-style multi-part form with a total of 20 fields. Each section of the overall form is 5 fields. After completing each of the 5 fields you click “Next”, which validates the currently mounted section of the form, then continues on to the next section.

If you were to check validity over the entire form while you were entering in the first 5 fields you would never be able to move on to the next 5 because the form would never be valid.

That being said, what you’re proposing isn’t necessarily poor logic or incorrect. I’m not opposed to there being a prop that you can pass to the form that would decided whether or not to validate all the fields or only the currently mounted fields, something like validateAllFields.