[checkbox] React Checkbox not sending onChange

TLDR: Use defaultChecked instead of checked, working jsbin.

Trying to setup a simple checkbox that will cross out its label text when it is checked. For some reason handleChange is not getting fired when I use the component. Can anyone explain what I'm doing wrong?

var CrossoutCheckbox = React.createClass({
  getInitialState: function () {
    return {
        complete: (!!this.props.complete) || false
      };
  },
  handleChange: function(){
    console.log('handleChange', this.refs.complete.checked); // Never gets logged
    this.setState({
      complete: this.refs.complete.checked
    });
  },
  render: function(){
    var labelStyle={
      'text-decoration': this.state.complete?'line-through':''
    };
    return (
      <span>
        <label style={labelStyle}>
          <input
            type="checkbox"
            checked={this.state.complete}
            ref="complete"
            onChange={this.handleChange}
          />
          {this.props.text}
        </label>
      </span>
    );
  }
});

Usage:

React.renderComponent(CrossoutCheckbox({text: "Text Text", complete: false}), mountNode);

Solution:

Using checked doesn't let the underlying value change (apparently) and thus doesn't call the onChange handler. Switching to defaultChecked seems to fix this:

var CrossoutCheckbox = React.createClass({
  getInitialState: function () {
    return {
        complete: (!!this.props.complete) || false
      };
  },
  handleChange: function(){
    this.setState({
      complete: !this.state.complete
    });
  },
  render: function(){
    var labelStyle={
      'text-decoration': this.state.complete?'line-through':''
    };
    return (
      <span>
        <label style={labelStyle}>
          <input
            type="checkbox"
            defaultChecked={this.state.complete}
            ref="complete"
            onChange={this.handleChange}
          />
          {this.props.text}
        </label>
      </span>
    );
  }
});

This question is related to checkbox onchange reactjs

The answer is


To get the checked state of your checkbox the path would be:

this.refs.complete.state.checked

The alternative is to get it from the event passed into the handleChange method:

event.target.checked

onChange will not call handleChange on mobile when using defaultChecked. As an alternative you can can use onClick and onTouchEnd.

<input onClick={this.handleChange} onTouchEnd={this.handleChange} type="checkbox" defaultChecked={!!this.state.complete} />;

If you have a handleChange function that looks like this:

handleChange = (e) => {
  this.setState({
    [e.target.name]: e.target.value,
  });
}

You can create a custom onChange function so that it acts like an text input would:

<input
  type="checkbox"
  name="check"
  checked={this.state.check}
  onChange={(e) => {
    this.handleChange({
      target: {
        name: e.target.name,
        value: e.target.checked,
      },
    });
  }}
/>

In material ui, state of checkbox can be fetched as

this.refs.complete.state.switched

In the scenario you would NOT like to use the onChange handler on the input DOM, you can use the onClick property as an alternative. The defaultChecked, the condition may leave a fixed state for v16 IINM.

 class CrossOutCheckbox extends Component {
      constructor(init){
          super(init);
          this.handleChange = this.handleChange.bind(this);
      }
      handleChange({target}){
          if (target.checked){
             target.removeAttribute('checked');
             target.parentNode.style.textDecoration = "";
          } else {
             target.setAttribute('checked', true);
             target.parentNode.style.textDecoration = "line-through";
          }
      }
      render(){
         return (
            <span>
              <label style={{textDecoration: this.props.complete?"line-through":""}}>
                 <input type="checkbox"
                        onClick={this.handleChange}
                        defaultChecked={this.props.complete}
                  />
              </label>
                {this.props.text}
            </span>
        )
    }
 }

I hope this helps someone in the future.


It's better not to use refs in such cases. Use:

<input
    type="checkbox"
    checked={this.state.active}
    onClick={this.handleClick}
/>

There are some options:

checked vs defaultChecked

The former would respond to both state changes and clicks. The latter would ignore state changes.

onClick vs onChange

The former would always trigger on clicks. The latter would not trigger on clicks if checked attribute is present on input element.


In case someone is looking for a universal event handler the following code can be used more or less (assuming that name property is set for every input):

    this.handleInputChange = (e) => {
        item[e.target.name] = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    }

Examples related to checkbox

Setting default checkbox value in Objective-C? Checkbox angular material checked by default Customize Bootstrap checkboxes Angular ReactiveForms: Producing an array of checkbox values? JQuery: if div is visible Angular 2 Checkbox Two Way Data Binding Launch an event when checking a checkbox in Angular2 Checkbox value true/false Angular 2: Get Values of Multiple Checked Checkboxes How to change the background color on a input checkbox with css?

Examples related to onchange

Why can't I change my input value in React even with the onChange listener React Checkbox not sending onChange Set Label Text with JQuery android on Text Change Listener Jquery select change not firing onchange event on input type=range is not triggering in firefox while dragging select2 onchange event only works once Dropdown using javascript onchange How to fire a change event on a HTMLSelectElement if the new value is the same as the old? jQuery - on change input text

Examples related to reactjs

Error: Node Sass version 5.0.0 is incompatible with ^4.0.0 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app Template not provided using create-react-app How to resolve the error on 'react-native start' Element implicitly has an 'any' type because expression of type 'string' can't be used to index Invalid hook call. Hooks can only be called inside of the body of a function component How to style components using makeStyles and still have lifecycle methods in Material UI? React Hook "useState" is called in function "app" which is neither a React function component or a custom React Hook function How to fix missing dependency warning when using useEffect React Hook? Unable to load script.Make sure you are either running a Metro server or that your bundle 'index.android.bundle' is packaged correctly for release