I am having a very annoying issue with React and checkboxes. The application I am working with requires a list of checkboxes that represent settings that are persisted in the back-end. There is an option to restore the settings to their original state.
At first, I created a component that has an object like a map of settings. Each setting has a key and a boolean value. Hence:
{
bubbles: true,
gregory: false
}
Is to be represented as:
<input type="checkbox" value="bubbles" checked="checked" />
<input type="checkbox" value="gregory" />
Now, it seems React is ignorant about how a checkbox is supposed to work. I don't want to set the checkboxes' values, I want the "checked" property.
Yet, if I try something like this:
<input
type="checkbox"
value={setting}
checked={this.settings[setting]}
onChange={this.onChangeAction.bind(this)}
/>
I get this warning:
Warning: AwesomeComponent is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: [some useless docs page I read several times to no avail]
So I decided to create another component to wrap each individual checkbox and I got this:
<input
type="checkbox"
checked={this.state.checked}
onChange={this.onChangeAction.bind(this)}
/>
Now the checked
is a property present directly in my state.
This yields the same warning, so I tried using defaultChecked
:
<input
type="checkbox"
defaultChecked={this.state.checked}
onChange={this.onChangeAction.bind(this)}
/>
Which makes the warning disappear, but now it is unable to reset the checked
value to the default one. So I tried playing with the method componentWillReceiveProps
, this way I am quite sure my state is correct, this.state.checked
is correct and the component renders again.
And it does. But the checkbox remains as it was originally.
For now I left that ugly warning and I am using checked
.
How do I fix this thing so the warning goes away?
I was thinking that perhaps there is a way to force-re-render the component, so it captures the new defaultChecked
value and uses it. But I don't know how to do that. Perhaps suppress the warning only for this component? Is that possible? Perhaps there is something else that can be done?
Amoebe's answer is correct, but I think there's a cleaner solution than the double bank (
!!
). Simply add a defaultProps property with valuefalse
forchecked
prop of your Checkbox component:You can assign your data to the state and then make use of the the checked property associated with the individual checkbox to set the state as
Sample Data in state is as
JSFIDDLE
The problem arises if you set the
checked
property of your checkbox tonull
orundefined
.Those are a "falsely" values in JS, however React treats a value of
null
as if the property was not set at all. Since the default state of a checkbox is unchecked, everything will work fine though. If you then setchecked
totrue
React thinks the property suddenly comes into existence! This is when React figures you switched from uncontrolled to controlled, since now the propchecked
exists.In your example you can get rid of this warning by changing
checked={this.settings[setting]}
tochecked={!!this.settings[setting]}
. Notice the double bang (!!
). They will convertnull
orundefined
tofalse
(and leavetrue
alone), so React will register yourchecked
property with a value offalse
and start off with a controlled form component.I had this problem too and I, too, read the docs about controlled-components serveral times to no avail, but I finally figured it out, so I thought I'd share. Also, since version 15.2.0 normal inputs are triggered to be controlled by setting
value
, while checkboxes are initialized as controlled by settingchecked
, regardless of theirvalue
property, which added a bit to the confusion.Basically, the
defaultChecked
means you don't want to control the input – it just renders with this value and then there is no way to control it. Also,value
shouldn't be used, butchecked
instead, so your second code should be correct. And you shouldn't use them both simultaneously.Can you create a small fiddle with this behaviour?