I'm trying to determine when at least one checkbox is checked in React.
I have a Checkbox component:
import * as React from 'react';
import * as DOM from 'react-dom';
import PropTypes from 'prop-types';
class Checkbox extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
<label>
<input
type="checkbox"
name={this.props.value}
value={this.props.value}
onClick={this.props.function}
/>
<span>
<span>
{this.props.title}
</span>
</span>
</label>
</div>
);
}
}
Checkbox.PropTypes = {
title: PropTypes.string,
value: PropTypes.string,
function: PropTypes.func,
}
export default NewsletterCheckbox;
It is imported and used in a parent component:
checkFields() {
// check if other fields are not empty and valid
console.log(this.state);
}
isCheckboxChecked(event) {
// determine if at least one checkbox is checked, and if so
// update state
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
const checkedOne = Array.prototype.slice.call(checkboxes).some(x => x.checked);
if (checkedOne) {
this.setState({ checkboxValid: true }, this.checkFields);
} else {
this.setState({ checkboxValid: false }, this.checkFields);
}
}
const newsletterArray = NewsletterData.map((obj) => (
<div key={obj.value}>
<div>
<Checkbox
value={obj.value}
title={obj.title}
function={this.isCheckboxChecked}
/>
</div>
<p>
{obj.body}
<br />
<a onClick={(e) => {this.handleModal(e) }} data-iframe={obj.link}>
See Preview
</a>
</p>
</div>
));
The issue I'm running in to is that the function that runs when any of the checkboxes are checked, isCheckboxChecked()
, once one field has been checked, if I uncheck that field (or check a bunch, and then uncheck them all), it never comes back as false.
Is there a better way of checking if at least one checkbox is checked in React without querying the DOM (which doesn't seem to work correctly with React rendered checkboxes)?
You should keep a counter of the checked checkbox
<input
type="checkbox"
name={this.props.value}
value={this.props.value}
onChange={this.props.onChange} />
constructor(props) {
super(props);
this.state = {checkCount: 0}
}
<Checkbox
value={obj.value}
title={obj.title}
onChange={(e) => {
if (e.target.checked)
this.setState((prevState) => ({checkCount: prevState.checkCount + 1, checkboxValid: true}))
else {
this.setState((prevState) => ({checkCount: prevState.checkCount - 1}), () => {
if (this.state.checkCount === 0)
this.setState({checkboxValid: false})
})
}
}} />
We are setting the checkCount to +1 or -1 if the user checks or unchecks the checkbox. If he unchecks it, after updating the checkCount, we need to verify this.state.checkCount === 0
in order to set the checkboxValid
state to false if the condition is valid.
The first condtion on the onChange handler throws an error. I'm assuming it needs to be wrapped in the arrow func like the one in the else condition? Either way, neither of these seem to update the state of either values (checkCount or checkboxValid).
I edited the answer (forgot some
()
:{checkCount: prevState.checkCount - 1}
=>({checkCount: prevState.checkCount - 1})
). You can check the live version here : codesandbox.io/s/angry-aryabhata-ub2qn