24

react gives me a 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)."

However my checbox is change via the state property. Am I missing something obvious?

    import React from 'react';

// Components
import Checkbox from './checkbox';
import HelpBubble from './helpBubble';

export default class CheckboxField extends React.Component {


    constructor(props) {
        super(props);
        this.state = {value: props.value};
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({value: event.target.value});
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.props.value) {
            this.setState({value: nextProps.value});
        }
    }

    render() {
        const {label, meta = {}, help, disabled, required, onChange} = this.props;

        return (
            <label className="checkbox-wrap form-field">
                <Checkbox
                    disabled={disabled}
                    type="checkbox"
                    onChange={(event) => {
                        onChange(event, !this.state.value);
                    }}
                    checked={this.state.value}/>
                {label && (
                    <div className="checkbox-label">
                        {label}
                        {required && <div className="form-field__required"/>}
                    </div>
                )}
                {help && <HelpBubble help={help}/>}
                {meta.error && meta.touched && (
                    <div className="input-error">{meta.error}</div>
                )}
            </label>
        );
    }}

Parent component: handleChangeParams(key, value) } /> Handle change params changes the value in model and calls server. Depending on server result, the value can change.

Thanks in advance.

Viktor
  • 521
  • 1
  • 4
  • 17
  • There's no way for us to know for sure without you also posting the `Checkbox` component and the parent component of `CheckboxField` – Peter Van Drunen Oct 30 '18 at 19:42
  • `onChange={(event) => { onChange(event, !this.state.value); }}` not sure, why thi? – Ved Oct 30 '18 at 19:44
  • Like @Ved is hinting, the handler is wrong. But this looks like it has even more problems. I suggest you check https://reactjs.org/docs/forms.html use their example and take it from there. – Yoshimitsu Oct 30 '18 at 19:49
  • @PeterVanDrunen I have added some info about parent component. The answer bellow seem to work, but I am still courious if I am doing anything wrong :) – Viktor Oct 31 '18 at 09:23
  • @Ved when "onChange" is invoked, the state value was still the "previous" value. In fact it did not change at all unless set by the parent element. – Viktor Oct 31 '18 at 09:26
  • @Yoshimitsu I have checked that, but it does not allow to set the value programatically from the parent. I suspect I would need to change the value of the element by copying the value from redux state into component state? – Viktor Oct 31 '18 at 09:29
  • @Viktor The accepted answer fixes your problem, but the real root of the issue is that when you hand a `null` or `undefined` value to the value attribute of an input element in React, React doesn't like it. You have to pass *something* so passing an empty string instead of `null` or `undefined` fixes the error. – Peter Van Drunen Oct 31 '18 at 13:17

6 Answers6

47

If your state is initialized with props.value being null React will consider your Checkbox component to be uncontrolled.

Try setting your initial state so that value is never null.

this.state = { value: props.value || "" };
mindlis
  • 1,546
  • 11
  • 17
8

If you are using a checkbox react won't like a string either so instead try

this.state = { checkboxValue: props.checkboxValue || false };
Andrew
  • 105
  • 2
  • 9
2

Something worth noting about the above code snippet. When you set a state in the constructor from props, it is always best to set the state to a "controlled" value i.e. a tangible value such as an int, float, string, array, map, etc. The error you are getting is the result of props.value being set to null

So, Consider setting your constructor state like this:

this.state = {
    value: props.value ? props.value : 'empty'
}

What is happening here is it is checking if props.value has a value, if it does it sets the state to props.value, if props.value is null, it sets the state to the string: `'empty'

J Dorrian
  • 206
  • 3
  • 15
1

Another simple way to do this would be to !! your props.checkboxValue value. That way even if it's undefined, !!props.checkboxValue will resolve to false.

this.state = { checkboxValue: !!props.checkboxValue };

David Corbitt
  • 111
  • 1
  • 3
1

do not use e.target.checked in the inputbox onChange eventHandler method.

Correct way:

  const [isChecked, setCheck] = useState(false);
 const handler = (e) => {
    setCheck(!isChecked);
  };

 <input
        type="checkbox"
        checked={isChecked}
        onChange={handler}
      />
GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
  • if I'm understanding this properly, isChecked is set to false initially, then a function is set to trigger on checkbox change which will change the isChecked to true if currently false and vice versa. My question here is what happens when I want the checkbox to be true on init? If I copy down that code, but set useState(true), I get the opposite result. I'm a react noob and I cannot find documentation on how the exclamation marks function – Edward Apr 01 '22 at 22:07
  • 1
    @Edward https://stackoverflow.com/questions/8012003/what-is-an-exclamation-point-in-javascript – GorvGoyl Apr 02 '22 at 10:08
0

In my case, I was using a prop from my redux store to set whether the checkbox was checked, simply defaulting the property to false worked for me.

e.g.

const MyComponent = ({
  somePropFromRedux
}) => {
  return <thatThridPartyCheckboxComponent checked={somePropFromRedux} />
}

becomes (only change is adding = false on Line 2)

const MyComponent = ({
  somePropFromRedux = false
}) => {
  return <thatThridPartyCheckboxComponent checked={somePropFromRedux} />
}
Michael
  • 2,973
  • 1
  • 27
  • 67