0

EDIT: this question was marked as a duplicate by some users. Not sure if they read it before doing so. If someone did, please clarify in which sense this is a duplicate.

I have a component for checkboxes:

class Checkbox extends Component {
    onChange = (e) => {
        if (this.props.input) {
            this.props.input.onChange(e.target.checked);
        } else if (this.props.onChange) {
            this.props.onChange(e.target.checked, this.props.id);
        }
    };

    render() {
        const { input, value, className, label } = this.props;
        let inputValue = (input && input.value) || value;

        return (
            <div className={'Checkbox' + (className ? ' Checkbox--' + className : '')}>
                <input
                    className="Checkbox-input"
                    type="checkbox"
                    onChange={this.onChange}
                    checked={inputValue}
                />
                <span className="Checkbox-helper" />
                <span className="Checkbox-label" htmlFor="">
                    {label}
                </span>
            </div>
        );
    }
}

This component returns an error when the value changes.

Warning: A component 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.

But if I replace:

let inputValue = (input && input.value) || value;

with

let inputValue = value;
if (input) {
    inputValue = input.value;
}

Like so:

class Checkbox extends Component {
    onChange = (e) => {
        if (this.props.input) {
            this.props.input.onChange(e.target.checked);
        } else if (this.props.onChange) {
            this.props.onChange(e.target.checked, this.props.id);
        }
    };

    render() {
        const { input, value, className, label } = this.props;

        let inputValue = value;
        if (input) {
            inputValue = input.value;
        }

        return (
            <div className={'Checkbox' + (className ? ' Checkbox--' + className : '')}>
                <input
                    className="Checkbox-input"
                    type="checkbox"
                    onChange={this.onChange}
                    checked={inputValue}
                />
                <span className="Checkbox-helper" />
                <span className="Checkbox-label" htmlFor="">
                    {label}
                </span>
            </div>
        );
    }
}

It doesn't return any error. Why?

  • 1
    Duplicate of [link](https://stackoverflow.com/questions/37427508/react-changing-an-uncontrolled-input), controlled (using state) – tarzen chugh May 23 '19 at 15:39
  • Possible duplicate of [React - changing an uncontrolled input](https://stackoverflow.com/questions/37427508/react-changing-an-uncontrolled-input) – ray May 23 '19 at 15:40
  • Not sure if anyone did read my example before marking it as a duplicate. –  May 23 '19 at 15:45
  • @tarzenchugh any explanation about why is a duplicate? –  May 23 '19 at 16:42
  • @rayhatfield want details –  May 23 '19 at 16:42
  • What is the `input` prop here? – ray May 23 '19 at 16:53
  • If there is a question, why was it marked as a duplicate? –  May 23 '19 at 16:54
  • Of course, thank you very much. `This.props.input` is a Final form —https://github.com/final-form/react-final-form— `` component. My question is why different syntax causes different result. –  May 23 '19 at 17:01

1 Answers1

1

One possibility—there's not enough information here to say for sure—is that input.value is present but false (or falsy), so you fall back to the value prop, which is undefined, and you end up setting checked to undefined on your input.

This results in an uncontrolled checkbox.

Then, on a subsequent pass, either input.value or props.value has changed and you set checked to a real value, which means it's now a controlled input and react emits the warning.

In your initial case you'll get the value prop even if input.value is explicitly false or 0 or an empty string:

// if input.value === false here you get
// the fallback value which may be undefined
let inputValue = (input && input.value) || value;

In your modified case…

let inputValue = value;
if (input) {
  inputValue = input.value;
}

…you've avoided that scenario because you're predicating it on the presence of input itself rather than input.value.

ray
  • 26,557
  • 5
  • 28
  • 27
  • Hm, now I understand… `a && b` doesn't evaluate `b` when the `a` is true, but when both `a` and `b` are true. And that's why if I use `let inputValue = input ? input.value : value:` there is no error. –  May 23 '19 at 17:29