12

I'm having a problem with a text input that I want to be controlled, but it needs to support an empty value. Here is my component:

import React, { Component, PropTypes } from 'react';
import { ControlLabel, FormControl, FormGroup } from 'react-bootstrap';

const propTypes = {
  id: PropTypes.string.isRequired,
  label: PropTypes.string,
  onChange: PropTypes.func,
  upperCaseOnly: PropTypes.bool,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};

export default class UppercaseTextField extends Component {
  constructor(props) {
    super();
    this.state = { value: props.value };
    this.onChange = () => this.onChange();
  }

  onChange(e) {
    let value = e.target.value;
    if (this.props.upperCaseOnly) {
      value = value.toUpperCase();
    }
    this.setState({ value });
    this.props.onChange(e);
  }

  render() {
    return (
      <FormGroup controlId={id}>
        <ControlLabel>{this.props.label}</ControlLabel>
        <FormControl
          type="text"
          onChange={this.onChange}
          defaultValue={this.props.value}
          value={this.state.value}
        />
      </FormGroup>
    );
  }
}

UppercaseTextField.propTypes = propTypes;

When this is initially mounted, props.value is commonly (though not always) set to ''. This makes React 15 unhappy, as value='' makes the value get dropped, so React thinks it's an uncontrolled input, even though it has an onChange.

The component works, but I don't like getting this warning in the console:

Warning: FormControl is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: http://facebook.github.io/react/docs/forms.html#controlled-components

This worked fine in 0.14.x without any warnings, but now 15 seems to not like it. How do I clean it up to keep the functionality but get rid of the warning?

Kelly Keller-Heikkila
  • 2,544
  • 6
  • 23
  • 35

1 Answers1

15

ensure this.state.value is not undefined on mount. You could do this in your constructor by setting this.state = {value: props.value || ''}; or by making props.value a required property.

gurch101
  • 2,064
  • 1
  • 18
  • 18
  • 1
    Unfortunately, this doesn't work. If I make it a required prop, I get a warning that it's missing because React ignores empty-string and null props values for purposes of require validation. I tried your other suggestion (`this.state = {value: props.value || ''}`), but the empty-string makes it so `FormControl` is not receiving the value prop (because React dropped it), so I get the same warning. Since `FormControl` is part of react-bootstrap, I don't want to get into changing it. Any other ideas that would work for React 15, since this is a new warning with that version? – Kelly Keller-Heikkila May 12 '16 at 16:09
  • 1
    I'm not sure I follow. React doesn't ignore empty string props. Here's a fiddle: https://jsfiddle.net/69z2wepo/41889/ – gurch101 May 12 '16 at 21:06
  • I saw it worked in your fiddle. Could it be something with React-bootstrap 0.29.3? – Kelly Keller-Heikkila May 12 '16 at 21:06
  • 1
    Nope. Here's the same example with 0.29.3 - https://jsfiddle.net/69z2wepo/41890/. Another issue I see in your original example is that you're setting both `defaultValue` and `value`. You shouldn't be doing that - setting `value` alone will suffice. – gurch101 May 12 '16 at 21:09
  • You're right, I shouldn't use both `defaultValue` and `value`. When I removed `defaultValue`, the warning disappeared, so that was the cause. Thanks! – Kelly Keller-Heikkila May 12 '16 at 21:21