Availity / availity-reactstrap-validation

Easy to use React validation components compatible for reactstrap.
https://availity.github.io/availity-reactstrap-validation/
MIT License
191 stars 70 forks source link

"Short-circuit" validation #104

Open galacticglum opened 5 years ago

galacticglum commented 5 years ago

Pretty much, I have a custom async validator that is used to check whether a user with the entered email already exists; however, I don't want to actually make the API call unless the email is valid (via the email validator).

In addition, I would like to only my the API call when the user is done typing; rather, than making an API call every time the input changes. However, I DO want the email and required validators to run whenever the input changes (as these are local and have little processing).

checkUserExists = _debounce((value, ctx, input, cb) => {
    if (!value || value === '') {
        cb(false);
        return;
    }
    clearTimeout(this.checkUserExistsTimeout);
    this.checkUserExistsTimeout = setTimeout(() => {
        this.props.userExists({['email']:value})
        .then(() => {
            cb('This email address is already taken.');
        }).catch(() => {
            cb(true);
        });
    }, 500);
});
// render
<BetterAvField type="email" groupAttrs={{ className: 'form-label-group' }}
    id="inputEmail" name="email"
    placeholder="Email Address" label="Email Address"
    validate={{
        required: {
            value: true,
            errorMessage: 'We need your email address.'
        },
        // run only when the user is done typing
        email: {
            value: true,
            errorMessage: 'That\'s not a valid email address.'
        },
        async: this.checkUserExists
    }} labelAfter />
TheSharpieOne commented 5 years ago

The validations are all done in parallel (async) and not in any specific order (though, it would probably end up being in the order of the keys defined on the validate prop object.) It's not a pipeline approach as it probably should have been (allowing for ordering and differentiating between local/sync and remote/async validation) In order to get what you want, you would need to import the email validation from the library and run it in your async validation function. Use debounce to avoid firing you function with every keystroke. You don't really need the timeout within the debounce.

import { debounce } from 'lodash';
import { AvValidator } from 'availity-reactstrap-validation';
//...
checkUserExists = debounce((value, ctx, input, cb) => {
  if(AvValidator.required(value) !== true || AvValidator.email(value) !== true) {
    cb(false);
    return;
  }
  this.props.userExists({['email']:value})
    .then(() => {
      cb('This email address is already taken.');
    }).catch(() => {
      cb(true);
    });
}, 500);
galacticglum commented 5 years ago

Okay, thanks. I tried using denounce without the timeout and I had experienced a lot of lag when I was typing.

I'll try giving your solution a go and see whether it works!