Patryk Gąsior
Patryk Gąsior

Reputation: 41

React checkbox does not set it state to checked on label click

i have a problem that my custom checkbox does not work, im not very familliar with React, and can't find any nice spot to learn it more either. Can you checkout my code and tell me what i did wrong ?

    import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

class Checkbox extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      checked: false
    };
    this.handleChange = this.handleChange.bind(this);
    this.checkbox = React.createRef();
    this.handleClick = () => {
      this.checkbox.current.click();
    };
  }

  componentDidMount() {
    this.setState({'checked' : this.props.checked});
  }

  handleChange() {
    this.setState({'checked' : !this.state.checked});
  }

  render() {
    let {
      className,
      label,
      ...attributes
    } = this.props;

    const classes = classNames(
      'checkbox',
      className,
    );

    return (
      <div className={classes}>
        <input id="checkbox_1" type="checkbox" checked={this.state.checked} onChange={this.handleChange} {...attributes} ref={this.checkbox} />
        <label for="checkbox_1" className="checkbox-label" onClick={this.handleClick}>{label}</label>
      </div>
    );
  }
}

Checkbox.defaultProps = {
  checked: false,
  className: '',
  label: null
};

Checkbox.propTypes = {
  checked: PropTypes.bool,
  className: PropTypes.string,
  onClick: PropTypes.func,
  label: PropTypes.string
};

export default Checkbox;

Problem is that this component does not get checked, but if i set state to true it does not uncheck, if i install a console log to either handleChange or handleClick, i get console.logged when i click, but only for the first click, im not sure what it means. In another post here i red that it could be problem where on click i check and uncheck so i create an infinite loop, but in that case i should have an infinite loop of console.logs i think. If my code is totally wrong feel free to edit it as you wish, but if its not to much of a hustle try to comment for me changed parts so i better understand whats going on :D

Thanks in advance.

Upvotes: 2

Views: 4384

Answers (3)

Blundering Philosopher
Blundering Philosopher

Reputation: 6805

Problem number one is that you are setting a default prop checked: false in defaultProps, and this prop is always being set on the input via {...attributes}, so you're effectively always writing that value to false, which is why state changes aren't making any difference.

To fix, you can either get rid of that default prop (and also get rid of setting the state in componentDidMount), or you could stop throwing all attributes data into the input element by default. Up to you.

Now you should be able to see the checkbox get checked / unchecked.

As for changing the property for to htmlFor on the label element, this actually will basically bind the label element to the input, so you won't have to implement the onClick handler for the label. So once you fix this property, just get rid of your onClick on the label entirely. Here's the final code:

All in all, here's what to change:

  1. Remove {...attributes} from the <input> element or remove checked from defaultProps
  2. Change the for property to htmlFor on the <label> element
  3. Remove the onClick event from the <label> element

Upvotes: 2

Bj&#246;rn Hei&#223;
Bj&#246;rn Hei&#223;

Reputation: 1728

Welcome to stackoverflow :)

I think your problem lies in

handleChange() {
  this.setState({'checked' : this.state.checked});
}

You're not changing the state but simply want to set it again with the same value. You should be fine when you change it to:

handleChange() {
  this.setState({'checked' : !this.state.checked});
}

EDIT:

So the real problem about the input not changing from checked to unchecked is handing over {...attributes} to the input. This seems to be linked to the "checked" property which interferes with the default checked value of the input element.

Moreover you should use the htmlFor attribute instead of for when working with react.

Upvotes: 2

Andrei Voicu
Andrei Voicu

Reputation: 750

This is also an HTML problem, you have to link the label with the input so they trigger together, see solution below, also this might help (https://www.w3schools.com/tags/tag_label.asp)

<h1>checkbox demo</h1>

<input type="checkbox">
<label>I don't activate the checkbox</label>

<hr>

<input type="checkbox" id="checkbox_1">
<label for="checkbox_1">I activate the checkbox</label>

<hr>

<label>
	<input type="checkbox">
	I also activate the checkbox
</label>

Upvotes: -1

Related Questions