react: input checkbox not updating after re-render
React version: 16.4.2
The checkbox in the DOM is not checked after a state change.
constructor(props) {
super(props);
this.state = {
checked: false
}
componentDidMount() {
this.setState({
checked: this.props.checked
});
}
render() {
let checked = <input type='checkbox' onChange={() => {
this.setState({checked: !this.state.checked});
}} defaultChecked={this.state.checked} />
return (
// JSX
)
}
Console logging checked, the first render returns a react.element type: 'input" with the following props
props:
defaultChecked: false
Second render after state change
props:
defaultChecked: true
However, in my app, the checkbox is not checked. If I explicit set the defaultChecked property to true, then it will be checked. If I default the state.checked to true, it will be checked. I’ve tried removing the onChange listener, suspecting that that may be the culprit, but it’s not.
My current workaround
constructor(props) {
super(props);
if (this.props.checked) {
this.state = {
checked: true
}
} else {
this.state = {
checked: false
}
}
}
Feels messy.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 24
Commits related to this issue
- Fix checkboxes and radio buttons not updating correctly https://github.com/facebook/react/issues/13477#issuecomment-489274045 — committed to wagtail/wagtail-review-ui by kaedroho 4 years ago
- Fix checkboxes and radio buttons not updating correctly https://github.com/facebook/react/issues/13477#issuecomment-489274045 — committed to wagtail/wagtail-review-ui by kaedroho 4 years ago
Also getting the same issue with checked attribute set to state, state updating, and not updating DOM. The DOM updates (reflecting the new checked value) as soon as I change anything else in the state. This happens on my project for checkbox and radio input.
I’m using a fully controlled input in both cases. The state variable is updated in React dev tools, and I also printed out the updated value before render. It just seems the DOM won’t update in this particular case.
Unfortunately I couldn’t repro this on codepen so there must be some way my project is set up (along with the other people reporting this) that is triggering this effect.
UPDATE I figured out that I had a parent component that had an onClick that was calling evt.preventDefault(). I removed that call and the checkbox and radio inputs now update in the DOM when state is updated. I’m not sure why preventDefault would prevent a render of the DOM in this specific case, but I’m also fairly new to React and don’t know the inner workings. Is anyone able to shed some light on this?
Here’s a Code Sandbox demonstrating the issue: https://codesandbox.io/s/v67qwj1870
For those with this problem, as @a1theredbull said, do not include preventDefault on the change function:
I’m seeing this problem as well, there’s no preventDefault call yet the checkbox does not reflect the state true/false for checked attribute.
To sum it up again. Components in React can be either:
Fully uncontrolled — in this case your custom
<Input>would takedefaultCheckedas a prop, and pass it to DOM<input>. The state would be inside the<Input>component itself. The prop value would only ever be used once, when the component is first rendered, and then ignored. The parent would have no way to “force” that state to become something else. If you ever need to reset the input, you’d have to mount it with a different key.Fully controlled — in this case your custom
<Input>would receive bothcheckedandonChangeas props, and pass them down to the DOM<input>. In this scenario,<Input>wouldn’t have any state at all. Instead, the<Parent>would need to manage its state. If you have many inputs the<Parent>, and writing a separatehandleChangehandler for each of them is annoying, you can use the same handler for them.Don’t try to mix controlled and uncontrolled paradigms in one component. That only makes you confused. If you receive something as a prop, don’t try to put it in state and then “sync” them. Instead, either keep it fully uncontrolled, or lift the shared state up and remove it from the child.
Hope this helps.
I think you’re confused about how props and state works.
If you want
checkedto be controlled by the parent you should remove it from the local state. Don’t try to “sync” props and state — instead, let the component acceptcheckedandonChangeas props from the parent that owns the state, and pass them down.Please read the documentation about lifting state up:
https://reactjs.org/docs/lifting-state-up.html
Similarly, despite the suggestion in this thread, you don’t want to use
getDerivedStateFromPropsfor this. Please read our blog post: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.htmlHope this helps!
Make sure the value you’re passing into
checkedis notundefined. If it is undefined, then the component will be uncontrolled, even if you have a validonChange(form3 in codepen link)Also, as stated by other people, make sure you’re not calling
evt.preventDefault()in your event handler (form4 in codepen link)Examples codepen: https://codepen.io/mrcoles/pen/WNbGwVw
This was it! I was trying to read data from state but initially, the key didn’t exist. That’s why my checkbox became uncontrolled and wasn’t reading latest data. This solved it.
checked={state.isPrescriptionSubmitted || false}Here, I’m wondering what the hell is wrong with my checkbox not updating after making sure everything is not wrong. I didn’t realize that I was using I freaking
defaultCheckedproperty instead ofcheckedon my checkbox. Damn, I was so dumb, it legit took me 30 minutes to figure it out.try:
static getDerivedStateFromProps(props) { return { editingQuark: checked:props.checked }; }