Alaa Ali
Alaa Ali

Reputation: 926

How do I disable a button based on multiple states in React?

I'm building a form with React and I wanted to disable a button when the form is not 'valid', i.e. when one of the states is blank. The button looks at a state called disable_button. Here's what I have so far:

var MyForm = React.createClass({
  getInitialState: function() {
    return {
      group_id: '',
      ls_type_id: '',
      disable_button: false
    };
  },
  handleGroupChange: function(e) {
    this.setState({group_id: e.target.value});
  },
  handleTypeChange: function(e) {
    this.setState({ls_type_id: e.target.value});
  },
  handleClick: function() {
    console.log('Button clicked');
  },
  componentDidUpdate: function() {
    this.isFormValid();
  },
  isFormValid: function(){
    if (this.state.group_id == '' || this.state.ls_type_id == '') {
      this.setState({disable_button: true});
    } else {
      this.setState({disable_button: false});
    }
  },
  render: function() {
    return (
      <div>
        <select className='form-control' value={this.state.group_id} onChange={this.handleGroupChange}>
          <option value=''>Select group...</option>
          <option value='10'>Some group</option>
        </select>
        <br /> 
        <select className='form-control' value={this.state.ls_type_id} onChange={this.handleTypeChange}>
          <option value=''>Select type...</option>
          <option value='11'>Some type</option>
        </select>
        <br />
        <button className="btn btn-primary" onClick={this.handleClick} disabled={this.state.disable_button}>Save</button>
      </div>
    );
  }
});

ReactDOM.render(<MyForm />, document.getElementById('content'));

Running this code results in an Uncaught RangeError: Maximum call stack size exceeded and I realize why: it's because after the component updates, it calls my componentDidUpdate, which then updates a state, which then updates the component, and the loop occurs. So I don't really know how can I make the button disable when one of those states is blank. I might add more form fields so I didn't want to hardcode it to only look at these two selects.

Upvotes: 2

Views: 7723

Answers (2)

yoz
yoz

Reputation: 952

Try this instead:

isFormValid: function(){
  return this.state.group_id == '' || this.state.ls_type_id == '';
}

...

<button disabled={this.isFormValid()} ...>

Upvotes: 4

Drew Schuster
Drew Schuster

Reputation: 2691

componentDidUpdate has two props passed in to it, prevProps and prevState as documented. You could use this fact in componentUpdate to only call isFormValid if either the type_id or ls_type_id has changed.

However, the cascading setState calls are a little inefficient. It might make more sense to determine disable_button in your render function, since you don't appear to be consuming it anywhere else. So, delete isFormValid and all calls to it, and put this in the beginning of render:

render: function () {
  var disable_button = false;
  if (this.state.group_id == '' || this.state.ls_type_id == '') {
    disable_button = true;
  }
  return ( <div> ...content </div>
}

Upvotes: 3

Related Questions