React checkbox doesn't toggle
I think I see what's happening.
You click the button, and it toggles is_checked
, which either checks or unchecks the box. But that ends up triggering an onChange
for the checkbox, which also toggles the state... You've actually coded an infinite loop. Although, since React batches/debounces setState
operations, your code won't lock your page up.
Try this:
2019 Update for hooks API:
import React, { useState } from 'react';
const Component = () => {
const [isChecked, setIsChecked] = useState(true);
return (
<div>
<input
type="checkbox"
onChange={(event) => setIsChecked(event.currentTarget.checked)}
checked={isChecked}
/>
<button onClick={() => setIsChecked(!isChecked)}>
change checkbox state using this button
</button>
</div>
);
};
Original:
var React = require("react");
var Component = React.createClass({
getInitialState: function() {
return {
isChecked: true
};
},
handleCheckboxChange: function(event) {
console.log("checkbox changed!", event);
this.setState({isChecked: event.target.checked});
},
toggleIsChecked: function() {
console.log("toggling isChecked value!");
this.setState({isChecked: !this.state.isChecked});
},
handleButtonClick: function(event) {
console.log("button was pressed!", event);
this.toggleIsChecked();
},
render: function() {
return (
<div>
<input type="checkbox" onChange={this.handleCheckboxChange} checked={this.state.isChecked} />
<button onClick={this.handleButtonClick}>change checkbox state using this button</button>
</div>
);
}
});
module.exports = Component;
Note that you can make this code even cleaner by using React's valueLink (read more here: https://facebook.github.io/react/docs/two-way-binding-helpers.html)
The reason for this behavior has to do with an implementation detail of React - more specifically the way how React normalizes change handling cross browser. With radio and checkbox inputs React uses a click event in place of a change event. When you apply preventDefault
within an attached event handler, this stops the browser from visually updating the radio/checkbox input. There are two possible workarounds:
- use
stopPropagation
alternatively - put the toggle into
setTimeout
:setTimeout(x => this.setState(x), 0, {is_checked: !this.state.is_checked});
Preferably you don't use preventDefault
at all, unless it is absolutely necessary.
Look into this React Github issue for further information.