React-redux-form: Disable form submit button if form is invalid, but enable it for async errors

Created on 15 Sep 2017  ·  13Comments  ·  Source: davidkpiano/react-redux-form

I have a use case that I need to disable the Submit button for a form until the form is in a valid state, e.g. all required fields are filled in. Now there are some fields that can receive server-side validation error messages after submit. When server-side validation returns error, the field async error states are set properly, and the validation messages are shown in the component. Now my problem is, that I'm unable to submit the form, as the Submit button still remains in a disabled state. I see that this behaviour is correct, but I'd like to know if there is a proper usage/workaround to somehow enable the Submit button or distinguish async error validation status on the form.

I tried to reset form validity, but this only resets the validity form the form, because setting errors on the fields happen AFTER the form validity state has been set.

Code sample:

  onSubmit = (payload) => {
    this.props.dispatch(actions.submitFields('deep.about_you.user', saveAboutYou(payload, this.props.registration)));
  };

   ...
// this.props.formValid comes from react-redux: state.deep.forms.about_you.user.$form.valid
   ...
<Button bsStyle="primary"
                        type="submit"
                        disabled={!this.props.formValid}>Next</Button>

...

export const saveAboutYou = (payload) => {
  delete data.user.outlet;
  return api.post('/api/users', payload)
                   .then((response) => {
                    ...
                   })
                   .catch((error) => {
                     store.dispatch(actions.resetValidity('deep.about_you.user'));
                     const data = {
                       ...error.response.data.errors
                     };
                     return Promise.reject(data);
                   })

};

enhancement

Most helpful comment

There's an internal helper function called isValid that you can pass an option to: isValid(form, { async: false }) which will return true if the form is valid, disregarding async validators. I'll expose it as an enhancement.

All 13 comments

There's an internal helper function called isValid that you can pass an option to: isValid(form, { async: false }) which will return true if the form is valid, disregarding async validators. I'll expose it as an enhancement.

I'd be a great help, thanks! My current solution is that I have custom components for input fields that can have server side validation, and I dispatch an actions.resetValidity() for the model in the component in the changeAction property. Do you think there is another way around, or I should go with this so far?

@davidkpiano I am having a similar issue and I'll make a separate report if it is not related. I have an onsubmit handler for the form and if i have local validations and type an invalid character into the field after it is valid and immediately hit the enter key the onsubmit handler fires with the form as valid as the onchange handler has not yet been able to update the state of the field. Seems to be a race condition and i am thinking of setting pending or something in the onchange handler to see if it may prevent this behavior.
I just checked and the data passed to submit is valid an doesn't contain the updated invalid data.

I figured out my issue! I have debounce set to 250. Of course the onchange isn't happening yet. however it may make sense to make a change to the onsubmit behavior to be aware of debounce and not set the form as valid if there are pending debounces. I know that may be tricky but thought I'd mention it.

@greghawk good idea, separate issue? 😄

will create now for easier documentation.

@davidkpiano You already have this working right. I had to add a hack onkeypress in the past to prevent the onchange from happening on enter key. This was making it skip onchange when firing submit. I did this so onsubmit wouldn't cause a secondary 3rd party validator call and stay stuck. I'll find if i can do another way.

@davidkpiano here’s what I did so you can se if you want to do something similar.

I am running async on change but only after local sync goes valid. This worked great except submit called on change again causing the enter key not to actually submit fully rather calling my async again since all my magic happens in onchangeaction.

I now store a key in redux with the name of the model + “verified” as soon as I get a good async ack. Then when enter key and submit are fired the onchangeaction checks for the key and if the currently passed value are the same and then returns if so.
This covers when they make any changes to the value once it has gone async valid once as well.

@greghawk Might be good to see code of that, if you can share.

@davidkpiano Here is my change action on a text control. It is a bit busy but this should illustrate my comment above. the validation named verification is my async.

             changeAction={(model, value, event) => (dispatch, getState) => {
               dispatch(actions.change(model, value));
               if (step.validation.verification !== null) {
                 if (getState().formState[`${step.attributeid}Verified`] !== value || getState().forms.formData[step.attributeid].validity.verification === false)
                   dispatch(actions.resetValidity(model, ['verification']));
                 if (getState().forms.formData[step.attributeid].valid && value.length > 0 && (getState().forms.formData[step.attributeid].validity.verification === undefined || !getState().forms.formData[step.attributeid].validity.verification)) {
                   dispatch(actions.setPending(model, true));
                   dispatch(actions.asyncSetValidity(model, (value, done) => getAsyncValidations(step.validation).verification(value).then(
                     (response) => {
                       if (response && step.actionsafterchange.length > 0)
                         dispatch(runActionsAfterStep(step)).then(() => {
                           dispatch(actions.setPending(model, false));
                           dispatch(actions.setTouched(model));
                           done(false);
                           dispatch(changeFormState(objectMapper.setKeyValue({}, `${step.attributeid}Verified`, value)));
                           dispatch(actions.setValidity(model, {verification: response}, {merge: true}));
                         }).catch((err) => {
                           dispatch(actions.setPending(model, false));
                           dispatch(actions.setTouched(model));
                           done({verification: false});
                         });
                       else {
                         dispatch(actions.setPending(model, false));
                         dispatch(actions.setTouched(model));
                         done(false);
                         if (response)
                           dispatch(changeFormState(objectMapper.setKeyValue({}, `${step.attributeid}Verified`, value)));
                         dispatch(actions.setValidity(model, {verification: response}, {merge: true}));
                       }
                     })));
                 }
               }
             }}

Hi @davidkpiano , I know it's been a while since this has been resolved, but there is something I have to add. I use Webstorm IDE for development, and If I try to use this _isValid_ utility function like in the documentation: import {isValid} from 'react-redux-form', Webstorm says _'cannot resolve symbol isValid'_. However, it seems to work in development. But it turns out it fails to work in production, an error raises on the console: index.js:125 Uncaught TypeError: n.i(...) is not a function. Do you have any idea what can cause this?
The scripts used for starting npm in dev mode and building are the following:

"start": "cross-env NODE_PATH=./src npm-run-all -p watch-css start-js",
"build": "npm run build-css && react-scripts build",

Did you look in rrf source to see if it is exported?

On Nov 3, 2017, at 4:23 AM, László Illés <[email protected]notifications@github.com> wrote:

Hi @davidkpianohttps://github.com/davidkpiano , I know it's been a while since this has been resolved, but there is something I have to add. I use Webstorm IDE for development, and If I try to use this isValid utility function like in the documentation: import {isValid} from 'react-redux-form', Webstorm says 'cannot resolve symbol isValid'. However, it seems to work in development. But it turns out it fails to work in production, an error raises on the console: index.js:125 Uncaught TypeError: n.i(...) is not a function. Do you have any idea what can cause this?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/davidkpiano/react-redux-form/issues/945#issuecomment-341643548, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AE2tk5UmEu0gq4YliVjzp-4huRgGDngZks5sys1rgaJpZM4PYujP.

@greghawk Yes, I looked into react-redux-form.d.ts and I can't find the _isValid_ string there. So I assume it's not exported, but I could find the method itself indeed in the source code elsewhere.

Was this page helpful?
0 / 5 - 0 ratings