blankface
blankface

Reputation: 6347

Buttons toggle in React

I'm trying to create a toggle button component with two buttons, where clicking on one disables the other and vice versa. I have a disabled state I know I have to toggle. But I'm not sure how to pan out the logic. Should there be one disabled state or two since there are two buttons?

  constructor(props) {
      super(props);
      this.state = { disabled: false }
    }

clicky(e) {
  //should dictate the toggle logic
}

render () {
  <div onClick={this.clicky.bind(this)}>
    <button disabled={this.state.disabled}>Item 1</button>
    <button disabled={this.state.disabled}>Item 2</button>
  </div>
}

Upvotes: 0

Views: 3645

Answers (4)

Chris
Chris

Reputation: 59541

Dane has the best answer (in my opinion) so far, and my answer is quite similar to his. Though I do want to make some further suggestions:

  • First one would be to move the event listener out of your div and attach it to each of your buttons. The main reason being improving accessibility (e.g for screen readers) because static elements such as div, p, etc don't have a semantic meaning. Besides, it kind of makes more sense to have the actual button trigger something, rather than the buttons wrapper.

  • My second suggestion is to move your binding of your clicky() function into the constructor instead. This will prevent your component from binding on each re-render and will instead only do so once on component mount.

Here is a slighly modified version of Danes solution:

constructor(props) {
  super(props);
  this.state = { button1Disabled: false }
  this.clicky = this.clicky.bind(this);
}

clicky() {
  this.setState(prevState => ({
    button1Disabled: !prevState.button1Disabled
  }));
}

render () {
  return (
    <div>
      <button onClick={this.clicky} disabled={this.state.button1Disabled}>Item 1</button>
      <button onClick={this.clicky} disabled={!this.state.button1Disabled}>Item 2</button>
    </div>
  )
}

Upvotes: 1

Dane
Dane

Reputation: 9582

This should do:

constructor(props) {
  super(props);
  this.state = { button1Disabled: false }
}

clicky() {
  this.setState(prevState => ({
    button1Disabled: !prevState.button1Disabled
  }));
}

render () {
  return (
    <div onClick={this.clicky.bind(this)}>
      <button disabled={this.state.button1Disabled}>Item 1</button>
      <button disabled={!this.state.button1Disabled}>Item 2</button>
    </div>
  )
}

Upvotes: 1

intentionally-left-nil
intentionally-left-nil

Reputation: 8336

Since the buttons are inversely related, one state is a better idea, because with two variables, they can get out of sync. Also, because only one button is enabled at a time, managing state is simple, you just need to toggle the state every time a button is clicked:

constructor(props) {
     super(props);
     this.item1 = React.createRef();
     this.item2 = React.createRef();
     this.state = { enableFirst: true }
   }

clicky(e) {
  const enableFirst = e.target !== item1;
  this.setState({ enableFirst });
}

render () {
 <div onClick={this.clicky.bind(this)}>
   <button ref={item1} disabled={this.state.enableFirst}>Item 1</button>
   <button ref={item2} disabled={!this.state.enableFirst}>Item 2</button>
 </div>
}

Upvotes: 0

Shubham Khatri
Shubham Khatri

Reputation: 282050

Instead of keeping a boolean, you would keep the button identifier in the disabled state, which can scale upto any number of buttons instead of just two

constructor(props) {
    super(props);
    this.state = { disabled: "1" }
}

clicky(e) {
  //should dictate the toggle logic
  const id = e.target.id
  this.setState({ disabled: id })
}

render () {
  <div onClick={this.clicky.bind(this)}>
    <button disabled={this.state.disabled === "1"} id="1">Item 1</button>
    <button disabled={this.state.disabled === "2"} id="2">Item 2</button>
  </div>
}

Working demo

Upvotes: 1

Related Questions