React-redux-form: Easy way to trigger total form validation?

Created on 24 Mar 2016  路  16Comments  路  Source: davidkpiano/react-redux-form

I'm wondering if there's a way to trigger my form to have all of its fields self-validate? For instance, if someone hits the submit button without touching several fields, I'd want those fields to self-check if they're required or not, and then prevent the form from submitting.

The only catch is, I can't use <Field /> because I'm using material-ui which wraps the input field too deeply, so I'm doing this:

          <TextField
            name="insights.title"
            floatingLabelText="Title"
            errorText={getError(props.fields, 'title')}
            onChange={props.onTextFieldChange}
            onBlur={props.onTextFieldBlur}
          />
enhancement question

Most helpful comment

@ffxsam I'm working on simplifying this. A near-future release will accurately prevent onSubmit for an invalid form, no matter where the validation came from (an action, inside <Field>, inside <Form>, etc.)

Any change in a field state should also change the form state, especially for .valid, .pristine, etc. states.

All 16 comments

if someone hits the submit button without touching several fields, I'd want those fields to self-check if they're required or not, and then prevent the form from submitting.

You can accomplish this by putting the field validators in <Form>. Sorry, the documentation is lacking in examples (I'll add it soon) but you can essentially do this:

function isRequired(val) {
  return val && val.length;
}

function handleSubmit(data) {
  //...
}

<Form model="insights"
  onSubmit={(data) => handleSubmit(data)}
  validators={{
    title: isRequired,
    foo: (val) => val == 'bar'
  }}>
  <TextField ... />
</Form>

With this, the validators of the <Form> will run on load, on every change (only for the fields that have changed), and on submit. If invalid, the function passed into onSubmit will not be called.

So is this correct?

      <Form
        model="insights"
        validators={validators}
        onSubmit={insight => props.onSaveInsight(insight)}
      >
validators = {
  title: {
    filled(value) {
      return value && value.length > 0;
    },
  },
  description: {
    filled(value) {
      return value && value.length > 0;
    },
  },
};

The submission still occurs. Though I think it actually might not be good to just block that from being triggered, because it might be nice to display a dialog to the user prompting them to fill out the form. So I might just stick with my original approach of validating on the fields only, and checking things in the onSubmit:

    if (!this.props.formTouched) {
      // Form hasn't even been touched
      return;
    }
    if (this.props.formValid) {
      // Don't allow submit if any field is in an invalid state
      this.props.dispatch(userRequestedSaveInsight(data));
    }

Though if they leave a required field empty, it doesn't stop them at all. And ideally, it would trigger an error in the field too.

This is what I'm after:

  1. User is presented with form. All fields are blank, no error messages displayed.
  2. User focuses on a required field. No error displayed (since they haven't finished with the field yet).
  3. User begins typing, but backspaces so the field is empty. Still no error displayed.
  4. User tabs out of field. Error now displayed under field.
  5. User clicks submit. All invalid fields display an error. (at this point, they would say "Field is required.")

I'd be thrilled if I could get this behavior.

Sure, you just need to do something like this:

(showAllErrors || myForm.fields.myField.touched)
&& !myForm.fields.myField.valid
&& '*Error!'

where showAllErrors is a prop that you set when the user clicks submit.

@greaber How do you mange the showAllErrors prop? I can only think of an enhancer of the form reducers supporting this flag. Can you maybe provide an example?

Still trying to wrap my head around this. One of the things that trips me up about this package is that I'm not sure which field-specific states will trigger a state change in the form (for validity). There's a lot going on, and I feel like things could be simpler.

@ffxsam I'm working on simplifying this. A near-future release will accurately prevent onSubmit for an invalid form, no matter where the validation came from (an action, inside <Field>, inside <Form>, etc.)

Any change in a field state should also change the form state, especially for .valid, .pristine, etc. states.

@ataube You could use component state for showAllErrors, or if you need access from outside the component, you could keep it in Redux. I am not using any further abstractions like a form reducer enhancer.

That's exactly what I was doing in the end as well, putting the showAllErrors flag in my local component state. Anyhow, it would also be nice to put this flag directly into the redux-form state because this information belongs to the form, doesn't it?

@davidkpiano What is your opinion about it? I think it's rather a common use case to show all errors at once or not.

@ffxsam Enhancements in 0.9.2 just landed, which enables onSubmit to be prevented if the form is invalid, no matter where the validation came from (from <Form>, <Field> validators, or even actions).

As far as this:

User clicks submit. All invalid fields display an error. (at this point, they would say "Field is required.")

Let's talk about this as an enhancement. How about having a property like submitAttempted or submitFailed?

Well.. first off, I wanna emphasize that this is your creation, so I don't want to overstep. :)

IMO, I feel like there are already way too many properties on the fields & form. It's difficult to remember and keep track of what they do. Simplification would be awesome.

Just brainstorming here: what about a boolean option one can set, so if submit is attempted and some fields are invalid, the error properties will be set for those fields?

IMO, I feel like there are already way too many properties on the fields & form. It's difficult to remember and keep track of what they do. Simplification would be awesome.

You're absolutely right, which is why I'm strongly considering #60 for version 1.0.

Just brainstorming here: what about a boolean option one can set, so if submit is attempted and some fields are invalid, the error properties will be set for those fields?

What would this look like in code? Trying to visualize this.

I do think a submitFailed flag would be useful, with this logic:

  • If not submitted, submitFailed = false
  • If submitting, submitFailed = false
  • If submit failed, submitFailed = true
  • If resubmitting, submitFailed = false

Would be good to have a retouched flag as well, that signals if a user has touched the form after a submit, with this logic:

  • If not submitted, retouched = false
  • If submitting and touched, retouched = false
  • If submitted and not touched, retouched = false
  • If submitted _then_ touched, retouched = true
  • If submit failed _then_ touched, retouched = true
  • If resubmitting/pending (regardless of current retouched state), retouched = false

Just brainstorming here: what about a boolean option one can set, so if submit is attempted and some fields are invalid, the error properties will be set for those fields?

@ffxsam That is exactly what I do using the .submitted state to see if there has been a submit attempt along with .valid.

Ah yes, I just got a chance to look at that proposal. Sounds great!

+1 having the .submitted state to behave as described in #60 would do the trick. This would allow me to remove the hacky showAllErros flag as local component state.

The .submitFailed and .retouched states have been added to the form state in 0.9.6! Documentation coming up.

Was this page helpful?
0 / 5 - 0 ratings