Reputation: 407
I have a checkbox list and another checkbox at the top to check them all, but when I check them all, I can't clear them anymore.
class App extends React.Component {
state = { checked: undefined }
selectAll = ({ target: { checked } }) => this.setState({ checked })
render() {
const { checked } = this.state
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
return <ul>
<input type='checkbox' onChange={this.selectAll} /> Check All
{arr.map(i => <li>
<input type='checkbox' checked={checked} />
<span>checkbox {i}</span>
</li>
)}
</ul>
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 0
Views: 669
Reputation: 11622
Over engineered but its a better solution where it handle also action if all checkboxes are selected it will check select all
checkbox.
This snippet based on this
function CheckBox({name, value, tick, onCheck}) {
return (
<label>
<input
type="checkbox"
value={value}
checked={tick || false}
onChange={onCheck}
/>
{value}
</label>
);
}
function CheckBoxList ({options, isCheckedAll, onCheck}) {
const checkBoxOptions = (
<div className="checkbox-list">
{options.map((option, index) => {
return (
<CheckBox
key={index}
value={option.value}
tick={option.checked}
onCheck={(e) => onCheck(option.value, e.target.checked)} />
);
})}
</div>
);
return (
<div className="checkbox-list">
<CheckBox
name="select-all"
value="ALL"
tick={isCheckedAll}
onCheck={(e) => onCheck('all', e.target.checked)}
/>
{checkBoxOptions}
</div>
);
}
class List extends React.Component {
//constructor(props) {
// super(props);
state = {
isAllSelected: false,
checkList: [
{
value: 1,
checked: false,
},
{
value: 2,
checked: false,
},
{
value: 3,
checked: false,
}
]
};
//}
onCheckBoxChange(checkName, isChecked) {
let isAllChecked = (checkName === 'all' && isChecked);
let isAllUnChecked = (checkName === 'all' && !isChecked);
const checked = isChecked;
const checkList = this.state.checkList.map((item, index) => {
if(isAllChecked || item.value === checkName) {
return Object.assign({}, item, {
checked,
});
} else if (isAllUnChecked) {
return Object.assign({}, item, {
checked: false,
});
}
return item;
});
let isAllSelected = (checkList.findIndex((item) => item.checked === false) === -1) || isAllChecked;
this.setState({
checkList,
isAllSelected,
});
}
render() {
return (
<div className="list">
<CheckBoxList
options={this.state.checkList}
isCheckedAll={this.state.isAllSelected}
onCheck={this.onCheckBoxChange.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(<List />, document.getElementById('root'))
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 2
Reputation: 14385
You'll need to control each checkbox in order to accomplish this. Otherwise your checkboxes only have controlled values once the select all option is selected.
I would store your checkboxes as an array of objects in state and do something like this:
class App extends React.Component {
state = {
boxes: [
{name: 0, checked: false},
{name: 1, checked: false},
{name: 2, checked: false},
{name: 3, checked: false},
{name: 4, checked: false},
{name: 5, checked: false},
{name: 6, checked: false},
{name: 7, checked: false},
{name: 8, checked: false},
{name: 9, checked: false},
]
}
selectAll = (e) => {
// Loop through every box and set checked to the value of the current checkbox
e.persist();
this.setState(prevState => ({
boxes: prevState.boxes.map(value => (
{...value, checked: e.target.checked }
))
}))
}
handleCheck = (e, name) => {
// Loop through each box and change the value of the corresponding box
e.persist();
this.setState(prevState => (
{
boxes: prevState.boxes.map((value) => {
if (name == value.name) {
return {...value, checked: e.target.checked};
}
return value;
})
}
))
}
render() {
return <ul>
<input type='checkbox' onChange={this.selectAll} /> Check All
{this.state.boxes.map(value => (
<li key={value.name}>
<input
type='checkbox'
checked={value.checked} // Now value is informed by state
// Pass the event and name to handler
onChange={(e) => this.handleCheck(e, value.name)}
/>
<span>checkbox {value.name}</span>
</li>
))}
</ul>
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 3
Reputation: 73926
This happened since same state checked
is used by all checkboxes and also you have not set any event handler to update the state when any of the checkboxes are clicked. Thus you are unable to clear any of the checkboxes.
Here is one solution to resolve this, but setting a separate checked state for each textbox, so that changing one doesn't affect others at all, other than when Check All is clicked.
class App extends React.Component {
state = { checkboxes: [{id:1, checked: false}, {id:2, checked: false}, {id:3, checked: false}] }
selectAll = ({ target: { checked } }) => {
let { checkboxes } = this.state
checkboxes.forEach(chk => chk.checked = checked)
this.setState({checkboxes})
}
select = ({ target: { value, checked } }) => {
let { checkboxes } = this.state
checkboxes.forEach(chk => {
if (chk.id === +value) chk.checked = checked;
})
this.setState({checkboxes: checkboxes})
}
render() {
const { checkboxes } = this.state
return <ul>
<input type='checkbox' onChange={this.selectAll} /> Check All
{checkboxes.map(i => <li>
<input type='checkbox' checked={i.checked} onClick={this.select} value={i.id}/>
<span>Checkbox {i.id}</span>
</li>
)}
</ul>
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 2