Reputation: 6347
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
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
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
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
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>
}
Upvotes: 1