4

I'm trying to add custom validation logic to my react-bootstrap forms, but isInvalid doesn't seem to be respected. As soon as validated={true} is set on the form, it always validates non-empty strings, regardless of isValid or isInvalid. The invalid message is displayed, but the field shows as green. See here:

enter image description here

Here is the offending code:

 <Form noValidate validated={formValidated} id="bidForm">
    <Form.Group className="mb-3" controlId="formBasicBidding">
       <Form.Label>Bid Amount</Form.Label>
       <InputGroup hasValidation className="mb-3">
           <InputGroup.Text id="inputGroupPrepend">$</InputGroup.Text>
           <Form.Control
              required
              value={bidAmount}
              isInvalid={!(parseInt(bidAmount) > 0) && formValidated}
              onChange={e => setBidAmount(e.target.value)}
           />
           <InputGroup.Text id="inputGroupAppend">.00</InputGroup.Text>
           <Form.Control.Feedback type="invalid">
               Please select a dollar amount {`>`} 0
           </Form.Control.Feedback>
        </InputGroup>
    </Form.Group>
    <...>
</Form>

I was able to get it to work using a pattern= regex on the input, but I'm planning to do more complex validation where a regex won't be sufficient. How can I get react-bootstrap to follow isValid and isInvalid properly? Thanks!

1 Answer 1

10
+150

The docs don't do a great job of making this clear, but the form-level validated attribute hooks directly into HTML5's native validation library and can't be used simultaneously with other validation techniques. So if you're using the native option and an input doesn't violate any of the native validation options (required, pattern, etc), then the input will be considered valid. Since you don't have any of the native validation attributes besides required on your bid input, "foo" is valid as far as React-Bootstrap is concerned.

(If you're thinking 'well that's bad code design to let you use both simultaneously', I'd tend to agree with you.)

You'll note that with your current code, if you enter "foo" and submit the form the element actually does have an is-invalid class applied to it, but it's also got the :valid psuedo-selector applied to it because the entire form was validated, which seems to take precedence from a CSS perspective:

enter image description here

The best solution here is to use either the native HTML5 validation option (detailed here) or roll your own (with or without a helper library like Formik), but not both simultaneously.

If you're trying to avoid validating until the input is actually in a "dirty" state (e.g. the user filled it out or submitted the form), you can do something like this (CodePen here):

        <Form.Control
          required
          value={bidAmount}
          isInvalid={!(parseInt(bidAmount) > 0) && (bidAmount.length || formSubmitted)}
          onChange={(e) => setBidAmount(e.target.value)}
        />
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for answering! The screenshot that showed both :valid and :invalid was helpful. I ended up migrating over to use Formik, and it's much easier to customize the validation now.
This is a great answer but I think there is one additional point here. You should use both use isValid and isInvalid properties together, and while a bit counter intuitive I found that if you dont specify the isValid property then it won't show the valid state (green border and tick) unless the Form validated proeprty is also true.
I would go further and say that it's bad design that the Form component ignores the isInvalid property of its children. Even Drupal, for all its bizarre quirks, gets this simple concept right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.