How do I clear Bootstrap 4 errors after I submit my React form?

Viewed 1136

I'm using React 16.13.0 with Bootstrap 4. I have the following component created, that I use in my React form ...

const Input = (props) => {
    return (  
  <div className="form-group">
      <FormLabel>{props.title}</FormLabel>
      <FormControl
            isInvalid={props.errors && Boolean(props.errors[props.name])}
            type={props.type}
            id={props.name}
            name={props.name}
            value={props.value}
            placeholder={props.placeholder}
            onChange={props.handleChange}
          />

      {props.errors && props.errors[props.name] && (
          <FormControl.Feedback type="invalid">
                 {props.errors[props.name].map((error, index) => (
                     <div key={`field-error-${props.name}-${index}`} className="fieldError">{error}</div>
                 ))} 
          </FormControl.Feedback>
      )}
  </div>
    )
}

export default Input;

Below is the handle form submit function as well as some of the container where I create my form ...

  async handleFormSubmit(e) {
    e.preventDefault();
    const NC = this.state.newCoop;
    delete NC.address.country;

    try { 
      const response = await fetch(FormContainer.REACT_APP_PROXY + '/coops/',{
        method: "POST",
        body: JSON.stringify(this.state.newCoop),
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
      });

      if (response.ok) {
        const result = await response.json();
        window.flash('Record has been created successfully!', 'success') 
        return result;
      }
      throw await response.json();
    } catch (errors) {
      console.log('_error_: ', errors);
      this.setState({ errors });
    }  
  }

...

  render() {
    if (this.state.coopTypes && !this.state.coopTypes.length) {
      return null;
    }
    return (
      <div>
        <form className="container-fluid" onSubmit={this.handleFormSubmit}>
            <FormGroup
                controlId="formBasicText">      

                <Input inputType={'text'}
                   title= {'Name'} 
                   name= {'name'}
                   value={this.state.newCoop.name} 
                   placeholder = {'Enter cooperative name'}
                   handleChange = {this.handleInput}
                   errors = {this.state.errors} 
                   /> {/* Name of the cooperative */}

The issue is, after I submit my form, if there were errors from the previous run, they are still displayed on the screen.

enter image description here

Is there a standard way to clear out Bootstrap error displays after a successful form submission?

2 Answers

Since you are using this.state.errors to provide the error information prop to your <Input> component, you simply need to update your handleInput(...) function to clear this.state.errors when your input value changes. The idea is something like this:

handleInput = (event) => {
  const errors = Object.assign({}, this.state.errors);
  const target = event.target;

  // remove the error value for a specific input only
  delete errors[target.name];
  this.setState({ errors });

  // Do whatever else needs to be done here for your input
}

Here's a working sample of how this works: https://jsfiddle.net/4gqr0cfa/

You need to add or remove form validation based on whether form is valid or not. To do this, add validated prop on the Form which is initially set to false.

How can i make it work?

Follow below mentioned steps:

  1. create a boolean state variable in the component containing the Form and its initial value should be false.

    const [validated, setValidated] = useState(false);
    
  2. add a prop named validated on the form and set its value equal to the boolean state variable created in step 1.

    <Form validated={validated}>
       ...
    </Form>
    
  3. When form is submitted, check if form is valid, if it is, set state variable, validated, to true.

Here's a working demo

function App() {
  const [ validated, setValidated ] = React.useState(false);
  const [ form, setForm ] = React.useState({
    fields: {
      name: { type: 'text', value: '', title: 'Name' },
      email: { type: 'email', value: '', title: 'Email' },
      password: { type: 'password', value: '', title: 'Password' }
    },
    errors: {}
  });

  const handleSubmit = (event) => {
    event.preventDefault();
    const errors = {};
    let invalid = false;

    Object.entries(form.fields).forEach(([k, v]) => {
      if (!errors[k]) errors[k] = [];
      
      if (!v.value) {
        errors[k].push(`${k} is required`);
        invalid = true;
      }

      if (k === 'email' && !v.value.includes('@')) {
        errors[k].push('enter a valid email address');
        invalid = true;
      }

      if (k === 'password' && v.value.length < 8) {
        errors[k].push('password should be atleast 8 characters long');
        invalid = true;
      }
    });

    setForm({ ...form, errors });

    if (invalid) {
      setValidated(false);
    } else {
      setValidated(true); 
    }
  };

  const handleChange = ({ target }) => {
    const updatedField = { ...form.fields[target.name], value: target.value };
    
    const fields = { 
      ...form.fields,
      [target.name]: updatedField
    };

    setForm({ ...form, fields });
  };

  return (
    <div style={{ maxWidth: '300px', margin: '10px auto'}}>
      <ReactBootstrap.Form noValidate validated={validated} onSubmit={handleSubmit}>
        {
          Object.entries(form.fields).map(([k, v]) => {
            return (
              <Input
                key={k}
                title={v.title}
                type={v.type}
                name={k}
                value={v.value}
                placeholder={v.title}
                handleChange={handleChange}
                errors={form.errors}
              />
            );
          })
        }
        <ReactBootstrap.Button type="submit">Submit form</ReactBootstrap.Button>
      </ReactBootstrap.Form>
    </div>
  );
}



const Input = (props) => {
    return (  
      <ReactBootstrap.Form.Group>
        <ReactBootstrap.FormLabel>{props.title}</ReactBootstrap.FormLabel>
        <ReactBootstrap.FormControl
          isInvalid={
            Array.isArray(props.errors[props.name])
             ? props.errors[props.name].length > 0
                 ? true : false
             : false
          }
          type={props.type}
          id={props.name}
          name={props.name}
          value={props.value}
          placeholder={props.placeholder}
          onChange={props.handleChange}
        />

        {props.errors && props.errors[props.name] && (
            <ReactBootstrap.FormControl.Feedback type="invalid">
                  {props.errors[props.name].map((error, index) => (
                      <div key={`field-error-${props.name}-${index}`} className="fieldError">{error}</div>
                  ))} 
            </ReactBootstrap.FormControl.Feedback>
        )}
      </ReactBootstrap.Form.Group>
    )
}


ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/1.1.0-rc.0/react-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>

<div id="root"></div>

You can also view the same demo on stackblitz

You can improve the above demo by indicating to the user that the input field is valid or invalid by adding green or red border repectively by using is-valid and is-invalid classes.

function App() {
  const [ validated, setValidated ] = React.useState(false);
  const [ form, setForm ] = React.useState({
    fields: {
      name: { type: 'text', value: '', title: 'Name' },
      email: { type: 'email', value: '', title: 'Email' },
      password: { type: 'password', value: '', title: 'Password' }
    },
    errors: {}
  });

  const handleSubmit = (event) => {
    event.preventDefault();
    const errors = {};
    let invalid = false;

    Object.entries(form.fields).forEach(([k, v]) => {
      if (!errors[k]) errors[k] = [];
      
      if (!v.value) {
        errors[k].push(`${k} is required`);
        invalid = true;
      }

      if (k === 'email' && !v.value.includes('@')) {
        errors[k].push('enter a valid email address');
        invalid = true;
      }

      if (k === 'password' && v.value.length < 8) {
        errors[k].push('password should be atleast 8 characters long');
        invalid = true;
      }
    });

    setForm({ ...form, errors });

    if (invalid) {
      setValidated(false);
    } else {
      setValidated(true); 
    }
  };

  const handleChange = ({ target }) => {
    const updatedField = { ...form.fields[target.name], value: target.value };
    
    const fields = { 
      ...form.fields,
      [target.name]: updatedField
    };

    setForm({ ...form, fields });
  };

  return (
    <div style={{ maxWidth: '300px', margin: '10px auto'}}>
      <ReactBootstrap.Form noValidate validated={validated} onSubmit={handleSubmit}>
        {
          Object.entries(form.fields).map(([k, v]) => {
            return (
              <Input
                key={k}
                title={v.title}
                type={v.type}
                name={k}
                value={v.value}
                placeholder={v.title}
                handleChange={handleChange}
                errors={form.errors}
              />
            );
          })
        }
        <ReactBootstrap.Button type="submit">Submit form</ReactBootstrap.Button>
      </ReactBootstrap.Form>
    </div>
  );
}



const Input = (props) => {
    return (  
      <ReactBootstrap.Form.Group>
        <ReactBootstrap.FormLabel>{props.title}</ReactBootstrap.FormLabel>
        <ReactBootstrap.FormControl
          isInvalid={
            Array.isArray(props.errors[props.name])
             ? props.errors[props.name].length > 0
                 ? true : false
             : false
          }
          type={props.type}
          id={props.name}
          name={props.name}
          value={props.value}
          placeholder={props.placeholder}
          onChange={props.handleChange}
          className={
             Array.isArray(props.errors[props.name])
             ? props.errors[props.name].length > 0 ? 'is-invalid': 'is-valid'
             : ''
          }
        />

        {props.errors && props.errors[props.name] && (
            <ReactBootstrap.FormControl.Feedback type="invalid">
                  {props.errors[props.name].map((error, index) => (
                      <div key={`field-error-${props.name}-${index}`} className="fieldError">{error}</div>
                  ))} 
            </ReactBootstrap.FormControl.Feedback>
        )}
      </ReactBootstrap.Form.Group>
    )
}


ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/1.1.0-rc.0/react-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.min.js"></script>

<div id="root"></div>

View this demo on stackblitz

You can read about the form validation in bootstrap at the official docs of react-bootstrap and bootstrap

Related