react: Controlled inputs with the potential for `undefined` value
Upgraded to React 15rc1 today, and a new warning appears to have cropped up:
Warning: SearchBox is changing a uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or viceversa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components
The component it’s referring to is indeed a controlled component for the duration of its lifetime:
<input
type="text"
className="typeahead"
onKeyUp={::this.controlsHandler}
onChange={::this.changeHandler}
value={this.props.search.get('term')}
ref={(i) => this._input = i}
/>
The problem, I’ve realized, is that on first render, this.props.search.get('term') returns undefined, because its value is only populated with text put into the input.
I expect this is a pretty common pattern for controlled components?
Otherwise loving the release - having all SVG attributes at my disposal is very exciting 😃
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 6
- Comments: 18 (1 by maintainers)
How about removing this warning? It really annoying and makes me write unnecessary checks. To my mind it is normal when in flux Store you have some data which is
undefinedResurrecting a zombie here. The number of newcomers struggling with this is so high… It migth be the most repeated react question in code sandbox. I think we all struggled with this once in our react learning and it’s crazy that 6 years after React keeps forbidding you to use
undefinedas a value and forcing to use' 'or similar.What if we actually want the value to be undefined or null?
Forcing people to use useEffect() or adding a logic layer to transform the
''values feels so weird.Example: When I erase a number input I certainly don’t want to send a
''. I want to send anullor interpreteindefined/nullas adeleteor similar.''is not delete, is an empty string, and thats a different thing.Uncontrolled components are rare, i guess it would be nice to indicate that they are uncontrolled somehow and just make everything controlled by default 🤔
Ultimately, what you are really trying to specify is “this component is controlled, but is currently empty”. It is intentional that you specify this by setting the value to be the empty string. When the value is specified as undefined, React has no way of knowing if you intended to render a component with an empty value or if you intended for the component to be uncontrolled. It is a source of bugs. This is why the warning exists.
It sounds to me that
this.props.search.get('term')should return the empty string instead of undefined, assuming the initial value of the search box is the empty string.Alternatively, you could do a null/undefined check in this component, before passing the value to the input.
We haven’t received too much push back on this one, so I think it’s uncommon enough that the benefit outweighs the cost. Therefore, we probably aren’t going to change this, so there is nothing actionable in this bug. However, if our thinking changes on this matter, we can certainly reconsider.
While i can see the point, that this might be a source of bugs. I kind of disagree on the first part. The value property is part of the props object. Therefor you can use Object.keys() to figure out if that key has been intentionally added to the props object or if it hasn’t been. The key will exist if you explicitely set the property even if its value is undefined. When going with this approach
<MyInput value={ undefined } />would be a controlled component and<MyInput />would be uncontrolled.+1, please it’s really annoying.
A bit silly to get this warning. I’m not actually changing an uncontrolled input to a controlled input just because the default value is
undefined. The element is controlled from the start. So the warning is wrong. So do I ignore the warning or do I implement a workaround for just this invalid warning by specifying|| ''? I’d prefer the first, but sadly then my console gets way too messy. Is it possible to disable warnings?Just hit this warning and realized that I actually do want to have undefined as the initial value otherwise how does the placeholder het rendered? The input box uses placeholder to render in case the initial value is undefined and setting the value to anything else stops the placeholder string from rendering.
This is just so annoying and doesn’t make a lot of sense. Another use case for undefined is when you have a number-only input, and you wish to be able to display an empty input, without the defaulted
0. Can we maybe get some traction on this?@MartijnHols careful using
||operator as a work-around: if the state variable is set to0orfalse, then comparison this will evaluate to''(empty string) and overwrite the field value. My work-around was to use a helper function, https://github.com/garudacrafts/fun-fp/tree/master/src/function/coalesce, to check forundefinedusingtypeofoperator and wrap all my variables, like so:value={coalesce(this.state.session.date, '')}.We had this exact problem:
We cycled through a few different options:
nullfor the “controlled but blank” case. Not a huge fan of having both null and undefined hanging around though''for the “controlled but blank” case. Works great for aTextField, but fails for aNumberFieldwhere there is no equivalent of an empty string. (0is not ideal here either because sometimes we genuinely want the field to be empty)if ('value' in props)to decide whether the component is controlled or uncontrolled. Very subtle/implicit though, requires us to spread...propsin both theTextFieldand anyone who wrapsTextFielde.g.NumberFieldcontrolled={ true | false }. But eeeh. Feels like too much mechanics for the caller to be worried aboutvaluea wrapper object likevalue={ { value: 'foo' } }, meaning ifvalueis present, the component is always controlled, if not, it is uncontrolled.^ I like this the most. It’s extra boilerplate, but it can be enforced with Typescript, and it’s reasonably explicit without requiring a whole new prop.
Quick addendum: why use uncontrolled components in the first place? I feel like as a general pattern it’s nice to have a component that only passes up a value to the parent when it is in a valid state; especially when you start building fields for more complex types like objects with multiple required fields, that take time and multiple keystrokes/clicks for the user to input.
BUT there are always going to be cases where you want the field to be controlled, so it’s pivotal to have a way to differentiate between them. And just checking
if (value === undefined)doesn’t cover the case where the value is intentionally undefined.Although I generally agree with the opinions in this thread, I cannot support this statement. I’ve worked on projects where uncontrolled inputs are practically the default in every form. They are generally way more performant and have lately gained a lot of traction with libraries like react-hook-form.
It conceptually makes sense for the input to take undefined as value, as it is (for example) a valid return/render value for components. Seems to me like this would require a switch to a
hasOwnPropertycheck inhttps://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/react-dom-bindings/src/client/ReactDOMInput.js#L40-L43
but I am not familiar enough with the react internals to say that this won’t cause a million other problems.
@jimfb One thing I don’t understand about the underlying idea is this: Uncontrolled components are supposed to use
fValueinstead ofvalue– why does that not lead to the reasoning that the use ofdefaultValuemeans that the component is uncontrolled?If both
defaultValueandvalueare undefined, the component would assume to be controlled, which should not really break anything, right? So that approach should cover both use cases and enableundefinedas a valid value forvaluewithout the warning.Also, I find @cdxOo’s argument quite compelling.