Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warning is changing a uncontrolled input of type radio to be controlled... #6779

Closed
ButuzGOL opened this issue May 16, 2016 · 30 comments
Closed

Comments

@ButuzGOL
Copy link

I have this code

       <input name='test' value={1} type='radio' />
       <input name='test' value={2} type='radio' defaultChecked />

https://jsfiddle.net/69z2wepo/42137/

And when I click on radio it gives me warning
Warning: Test is changing a uncontrolled input of type radio 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: https://fb.me/react-controlled-components

So its not clear why or is it a bug ?

@syranide
Copy link
Contributor

It's a bug. 👍

I think it's reacting to value being set without considering that it is a radio-input which uses value differently.

@macgyver
Copy link

I have a related issue, I'm getting a warning that I'm switching an uncontrolled component to a controlled one with this code:

function CheckboxButton({checked, onChange, textComponent} = {}) {
    return (
        <label className='checkbox-button'>
            <input
                type='checkbox'
                checked={checked}
                onChange={onChange}/>
            {textComponent}
        </label>
    );
}

If I add a useless value prop then there is no warning. I think the warning code should acknowledge that the value property of a checkbox does not indicate whether it's controlled or not, but rather the checked prop.

@syranide
Copy link
Contributor

cc @zpao @spicyj sounds like we want to get this fixed.

@zpao
Copy link
Member

zpao commented May 19, 2016

Yea, I saw this myself yesterday.

@zpao zpao added the Type: Bug label May 19, 2016
@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

This bug is a bit annoying. For one thing, there are many different input types to consider (https://www.w3.org/TR/html5/forms.html#attr-input-type) and browsers seem to support more types than the spec indicates (for instance, Mozilla appears to support "month" as per https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).

In the particular case of a radio button, this is further complicated by the fact that you can't determine the "controlledness" of a group just by looking at a single input. You need to look at all the inputs with the same name prop within the same form group. Yuck.

Too bad we can't just ban uncontrolled components. That would make life so much easier. :P.

@syranide
Copy link
Contributor

This bug is a bit annoying. For one thing, there are many different input types to consider (https://www.w3.org/TR/html5/forms.html#attr-input-type) and browsers seem to support more types than the spec indicates (for instance, Mozilla appears to support "month" as per https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).

@jimfb Not sure if I'm following, as far as I understand it and is concerned it's just about checking whether type="radio" or type="checkbox" and then testing checked instead of value, it seems value is being tested regardless right now. The other types shouldn't really matter because they all rely on value.

In the particular case of a radio button, this is further complicated by the fact that you can't determine the "controlledness" of a group just by looking at a single input. You need to look at all the inputs with the same name prop within the same form group. Yuck.

Mixing controlled and uncontrolled radios is a problem too, but an entirely separate problem IMHO. This warning is simply about switching an individual input between being controlled and uncontrolled. I.e. checked should not go from null to non-null or vice versa on an individual component.

Too bad we can't just ban uncontrolled components. That would make life so much easier. :P.

I'm still of the opinion that they should be entirely separate components.

@psimoneau22
Copy link

psimoneau22 commented May 20, 2016

im experiencing this also: clicking the radio shows the error

<div className="radio">
                                        <label>
                                            <input type="radio" name="type" value="In Person" defaultChecked={true} />
                                            <span> In Person</span>
                                        </label>
                                    </div>
                                    <div className="radio margin-left-20">
                                        <label>
                                            <input type="radio" name="type" value="Phone" />
                                            <span> Phone</span>
                                        </label>
                                    </div>
                                    <div className="radio margin-left-20">
                                        <label>
                                            <input type="radio" name="type" value="Webex" />
                                            <span> Webex</span>
                                        </label>
                                    </div>

@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

@syranide I think it's a bit more complicated than that. Think about how you would implement these warnings for switching between controlled and uncontrolled radio buttons.

If any of the radio inputs within that form group have the checked attribute specified, then the whole group is controlled. Controlledness can't be tracked on a per-node basis for radio buttons.

@zpao
Copy link
Member

zpao commented May 20, 2016

Let's scope this to handling the 90% case which is just that this is warning incorrectly. We can worry about making it perfect some other time.

@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

@zpao That's what I'm talking about - this warning firing incorrectly (too often) for radio buttons.

if we just want a quick fix, I think we need to just disable this warning for radio buttons. I don't think there is a 90% solution for radio buttons that won't occasionally fire spurious warnings. A "correct" fix will require tracking all the radio inputs within each form group.

@zpao
Copy link
Member

zpao commented May 20, 2016

The test case in the initial report is warning and it should not be. It is not because of needing to track across radios in this case it's because we're incorrectly looking at value for radios and checkboxes (right here: https://github.com/facebook/react/blob/master/src/renderers/dom/client/wrappers/ReactDOMInput.js#L169-L171). We should not be falling back to value, we should be using the type to determine if we should be looking at checked or value.

You are talking about a real problem but it's tangential to this one.

@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

@zpao Right, but an uncontrolled radio group is not required to set a defaultChecked on any of the elements. And a controlled radio group is allowed to set a defaultChecked on a controlled element (for the reset button). This means that unless we do the smarter logic, any check we do is wrong and will fire spurious warnings. We might as well just remove the check (for radio buttons) at that point.

@syranide
Copy link
Contributor

@syranide I think it's a bit more complicated than that. Think about how you would implement these warnings for switching between controlled and uncontrolled radio buttons.

If any of the radio inputs within that form group have the checked attribute specified, then the whole group is controlled. Controlledness can't be tracked on a per-node basis for radio buttons.

@jimfb Huh? Isn't this how you use radio-buttons https://jsfiddle.net/dt03qw4p/? Just like value for text inputs, if checked is null, then it is uncontrolled, if it is non-null then it's controlled and depending on truthiness it's checked or not... right? It doesn't have to check the other radios for this scenario, it just has to check if checked changes from being null to non-null or vice versa (i.e. changing controlledness).

Or what am I missing?

@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

@syranide Are you required to specify checked={false} if the radio button is not checked in a controlled component? Today, the answer is no. Maybe we could/should change that, but it's a much wider discussion and almost certainly not within the scope of a 90% quick fix that @zpao wanted.

Example: this is a legal (immutable) controlled form:

<form>
  <input type="radio" name="foo" value="1" onChange={()=>{}} />
  <input type="radio" name="foo" value="2" onChange={()=>{}} checked />
  <input type="radio" name="foo" value="3" onChange={()=>{}} />
</form>

Obviously it's a contrived example. You can imagine that the user has a list of elements and just "sets" the checked attribute on the one that is checked. But the result is the same.

The "controlledness" of the input is defined by the entire group within the form. The lack of a checked attribute is insufficient to know if a particular input element is controlled. In this case, the controlledness of the first input is determined by the controlledness of the second.

That's why this warning is so tricky for radio buttons.

@syranide
Copy link
Contributor

syranide commented May 20, 2016

@syranide Are you required to specify checked={false} if the radio button is not checked in a controlled component? Today, the answer is no.

@jimfb Actually the answer is yes in a sense...? https://jsfiddle.net/hx2r7bc4/ (contrived example, but still)

Also, I would consider it very weird for someone to have a dynamic controlled component and not explicitly set checked (I doubt you'll find anyone that does even), additionally, this is a warning and basically intended to shine light on bad behavior so I'm not really sure if I would consider it a problem anyway?

Example: this is a legal (immutable) controlled form:

It works, I'm not sure if I would agree that it's a good idea. Regardless, that wouldn't be a problem, since checked is not provided a property to the others you can almost be certain that these are static radio buttons and not dynamic. So they would never change controlledness and warn anyway. Further, you should not use that pattern to create static radio buttons, that's what readOnly is for.

So again, this seems like a case of win-win to me, if you're doing it wrong/badly you get a warning. No one doing it right will get warning.

Perhaps I've missed something, but I don't see any case involving good practice that would actually cause you to see the warning, only bad ones.

@macgyver
Copy link

I withdraw my earlier comment, the spec indicates that value is a required attribute for checkboxes (even if I don't plan to use it)

https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox)

@macgyver
Copy link

macgyver commented May 20, 2016

correction, MDN indicates that it's a required attribute: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox

The spec doesn't actually seem to say that.. I'm not sure what the citation for MDN is but it seems incorrect to me. Personally I'd rather omit the value property for checkboxes, it seems perfectly sane for the browser to default it to on or something if necessary.

@jimfb
Copy link
Contributor

jimfb commented May 20, 2016

Actually the answer is yes in a sense...? https://jsfiddle.net/hx2r7bc4/ (contrived example, but still)

Wow, that sucks. That is the most terrible thing I've seen all month. But I'll give you credit because it demonstrates a problem with not specifying the checked prop on a controlled component when all values are false.

Also, I would consider it very weird for someone to have a controlled component and not explicitly set checked (I doubt you'll find anyone that does even), additionally, this is a warning and basically intended to shine light on bad behavior so I'm not really sure if I would consider it a problem anyway?

I'm not sure it's that weird. It is pretty intuitive/natural to loop over some data and set checked IFF the data says that value is checked (assuming you aren't using jsx). I'm not even convinced it's bad behavior (except given your fiddle above).

Anyway, I suppose I'm fine with banning that behavior 👍. I think that will work.


The only other edge case I'd worry about is what happens if a controlled checkbox turns into a controlled text input, but that should be easy for us to get right, we just need to test it after making our change(s).

@syranide
Copy link
Contributor

I'm not even convinced it's bad behavior (except given your fiddle above).

Just for posterity. We can't realistically get away from nullness of checked determining the controlledness with the current design. Given that a null checked effectively means uncontrolled I would say that it is wrong to mix them. Now it just so happens that you can rely on that pattern as long as one is always checked, but that is not guaranteed to be true. There may be values used that does not correspond to any radio (not necessarily an error), if you did it the "wrong way" then some radio would still be checked.

The only other edge case I'd worry about is what happens if a controlled checkbox turns into a controlled text input, but that should be easy for us to get right, we just need to test it after making our change(s).

IMHO, I think I had a PR about that that would consider it the same as remounting the component which I think everyone agreed but it never ended up merged. So until something like that happens I wouldn't even worry about it, it's virtually an error in a sense to change the type of an input today (there are other broken edge-cases too AFAIK).

@LiJinyao
Copy link

LiJinyao commented May 27, 2016

I have the same problem. This is my code:

<input type="checkbox" onChange={this.handlerMustfillChange} checked={this.state.isMustFill}/>

And when I click the checkbox I got the same warning.
warning.js:44 Warning: QuestionHeader is changing an uncontrolled input of type checkbox to be controlled.

@gaearon
Copy link
Collaborator

gaearon commented May 27, 2016

@LiJinyao

It means this.state.isMustFill is not always true or false, but rather true or undefined, for example. Make sure it’s always boolean, and the problem will go away.

@littlee
Copy link

littlee commented Jun 5, 2016

Is this issue still be considered as bug?
I change to use defaultValue with defaultChecked to make it no warning.

<input type="radio" name="a" defaultValue="1" defaultChecked/>
<input type="radio" name="a" defaultValue="11"/>

@jimfb
Copy link
Contributor

jimfb commented Jun 9, 2016

@littlee I don't blame you, I blame us, but that's just terrible.

@Plortinus
Copy link
Contributor

@gaearon
When a component has a value prop and at the same time a defaultValue(or defaultChecked) prop, it would be regard as an uncontrolled component.

I think this should be explicitly declared with the examples in the Form docs , for example write a Note about this situation, or it would missunderstands the React beginers.

@kopahead
Copy link

kopahead commented Jun 22, 2017

@littlee, in short you mean Instead of checked={this.state.isMustFill} do checked={!!this.state.isMustFill}

@littlee
Copy link

littlee commented Jun 23, 2017

@kopahead

Instead of checked={this.state.isMustFill} do checked={!!this.state.isMustFill}

I don't think it's related to what I said before

@sharpensteel
Copy link

sharpensteel commented Aug 31, 2017

i had same warning. casting value from null to false solves the problem:
<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >

so now i should put !! everywhere in checkboxes

sdob added a commit to gallmarch/fl-conversion-helper that referenced this issue Oct 3, 2017
Per facebook/react#6779
React gets confused if the expression controlling
the 'checked attribute on a checkbox is initially
null (or, presumably, undefined). Force-casting to a Boolean removes the
warning without changing the functionality.
@jeremybradbury
Copy link

jeremybradbury commented May 30, 2018

try: checked={(this.state.someValue)} instead which casts to boolean. falsey null/undefined/0/'' = false everything else true. this works just like a ternary operator:

checked={(this.state.item5) ? true : false}

@ryanneary
Copy link

Yet another way is:

checked={this.state.someValue === true}

@imtmh
Copy link

imtmh commented Jan 10, 2020

In my case, the checked value was passed as undefined when the component is loaded. I have passed false when it is undefined.

I used the following code, the warning disappeared.

function CheckboxButton({checked, onChange, textComponent} = {}) {
    return (
        <label className='checkbox-button'>
            <input
                type='checkbox'
                checked={checked || false}
                onChange={onChange}/>
            {textComponent}
        </label>
    );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests