I have just found a stray event.preventDefault()
that was breaking my checkboxes' onChange
handler:
import { Component } from 'react';
class App extends Component {
constructor(props) {
super(props)
this.state = {
accepted: false
}
}
changeChecked = (event) => {
this.setState((state) => ({
accepted : !state.accepted
}));
event.preventDefault(); // <- this very bad
}
render() {
return (
<input
type="checkbox"
onChange={this.changeChecked}
checked={this.state.accepted}
/>
);
}
}
export default App;
The resulting behaviour is a correctly updated state on first click, but the checkbox only changes to its 'checked' appearance on the next rerender, eg. a second click.
Why is that? Isn't it the point of controlled ponents to work independently from browser events?
Someone explaining this to me would definitely ease the pain of hours spent boiling down my plex use case. Thank you!
Update: Here's a quick Codepen example demonstrating the odd behaviour.
I included an 'unprevented checkbox' and one with a prevented onClick
event as parison.
Notice how the one with prevented onChange
switches its appearance to its actual state as soon as I click a different checkbox.
I have just found a stray event.preventDefault()
that was breaking my checkboxes' onChange
handler:
import { Component } from 'react';
class App extends Component {
constructor(props) {
super(props)
this.state = {
accepted: false
}
}
changeChecked = (event) => {
this.setState((state) => ({
accepted : !state.accepted
}));
event.preventDefault(); // <- this very bad
}
render() {
return (
<input
type="checkbox"
onChange={this.changeChecked}
checked={this.state.accepted}
/>
);
}
}
export default App;
The resulting behaviour is a correctly updated state on first click, but the checkbox only changes to its 'checked' appearance on the next rerender, eg. a second click.
Why is that? Isn't it the point of controlled ponents to work independently from browser events?
Someone explaining this to me would definitely ease the pain of hours spent boiling down my plex use case. Thank you!
Update: Here's a quick Codepen example demonstrating the odd behaviour.
I included an 'unprevented checkbox' and one with a prevented onClick
event as parison.
Notice how the one with prevented onChange
switches its appearance to its actual state as soon as I click a different checkbox.
2 Answers
Reset to default 5Checkboxes behave a little differently. As you likely know, the typical use case for preventDefault()
is the onSubmit()
function of a form where you will do your own AJAX call, and thus want to prevent the default form submission. But with checkboxe (and most inputs) there's a little more involved.
Checked attribute
Per MDN, the checked
attribute is "A Boolean attribute indicating whether or not this checkbox is checked by default (when the page loads). It does not indicate whether this checkbox is currently checked: if the checkbox’s state is changed, this content attribute does not reflect the change." In a funny way then, there's a disconnect between the checked
attribute, and whether or not the state of the input is checked or not. When it es to React, on each rerender, the checked
attribute will reflect the current state, but it's still only a default value in the sense that the input has been newly rendered, and not manipulated since the state last changed.
Native eventListener
for <input type="checkbox" />
Also, without getting too off track, the event that natively changes the state on a checkbox input is actually the click
event, not the change
event. If you were to mess with the listeners and the values of a checkbox input in your browser's js console, you'd see you could manipulate it in a way that the checkbox isn't checked, but the values of the node say otherwise.
Possible solution
Based on the above, in this case you don't want to prevent the default behavior, because the default behavior on the change
event doesn't conflict with what you want to do (and for some reason preventing it causes problems). In fact, in the Mozilla docs examples, you'll notice they don't use preventDefault()
when updating state on controlled ponents. I wish I had a better understanding of exactly why adding preventDefault()
to change handlers for inputs causes problems like this, but hopefully these few little tidbits give some more clarity.
Try moving event.preventDefault()
above this.setState
line.