I've got a list of items, each with their own checkboxes and I've decided to try add a 'select all' checkbox to make it easier for the user to select them all at once.
Unfortunately I'm finding it hard to work out the logic in a 'React' kinda way.
I found a JSBIN of how I would like the rendered result to work - https://jsbin.com/jetalaxaha/edit?html,js,output note: this is setup in a different way to how I would like it to.
My current code is here:
import React, { Component } from "react";
import ReactDOM from "react-dom";
class Items extends Component {
state = {
categories: [
{
id: 1,
name: "category 1",
items: [
{ name: "item 1", id: Math.floor(Math.random() * 99999) },
{ name: "item 2", id: Math.floor(Math.random() * 99999) }
]
},
{
id: 2,
name: "category 2",
items: [
{ name: "item 3", id: Math.floor(Math.random() * 99999) },
{ name: "item 4", id: Math.floor(Math.random() * 99999) }
]
},
{
id: 3,
name: "category 3",
items: [
{ name: "item 5", id: Math.floor(Math.random() * 99999) }
]
}
],
checkedListAll: [],
ItemsChecked: false
};
selectedItems(e) {
const { value, checked } = e.target;
let { checkedListAll } = this.state;
if (checked) {
checkedListAll = [...checkedListAll, value];
} else {
checkedListAll = checkedListAll.filter(el => el !== value);
if (this.state.ItemsChecked) {
this.setState({
ItemsChecked: !this.state.ItemsChecked
});
}
}
this.setState({ checkedListAll });
}
selectItem(e) {
const { checked } = e.target;
const { categories } = this.state;
const collection = [];
if (checked) {
this.setState(
{
checkedListAll: []
},
() => {
for (const cat of categories) {
for (const item of cat.items) {
collection.push(item.id);
}
}
this.setState({
checkedListAll: collection
});
}
);
} else {
this.setState({
checkedListAll: []
});
}
this.setState({
ItemsChecked: !this.state.ItemsChecked
});
}
render() {
const { categories, checkedListAll, ItemsChecked } = this.state;
return (
<div>
<header>
<label>
<input
type="checkbox"
checked={ItemsChecked}
onClick={this.selectItem.bind(this)}
/>Select all
</label>
</header>
{categories.map(cat => {
return (
<ItemCategory
{...cat}
key={cat.id}
click={this.openModal}
selectedItems={this.selectedItems.bind(this)}
ItemsChecked={ItemsChecked}
/>
);
})}
{
<pre>
All Selected: {JSON.stringify(ItemsChecked, null, 2)}
</pre>
}
{
<pre>
Selected List: {JSON.stringify(checkedListAll, null, 2)}
</pre>
}
</div>
);
}
}
class ItemCategory extends Component {
render() {
const { items, name, selectedItems, ItemsChecked } = this.props;
const getItems = items.map(item => {
return item;
});
return (
<div>
<div>-{name}</div>
<ul>
{getItems.map(item => {
return (
<li key={item.id}>
<Checkbox
item={item}
selectedItems={selectedItems}
ItemsChecked={ItemsChecked}
/>
</li>
);
})}
</ul>
</div>
);
}
}
class Checkbox extends Component {
state = {
isChecked: false
};
componentDidUpdate(prevProps) {
if (prevProps.ItemsChecked !== this.props.ItemsChecked) {
this.setState({
isChecked: !this.state.isChecked
});
}
}
handleClick(e) {
e.persist();
if (this.props.ItemsChecked) {
console.log(true);
}
this.setState(
{
isChecked: !this.state.isChecked
},
() => {
this.props.selectedItems(e);
}
);
}
render() {
const { item } = this.props;
const { isChecked } = this.state;
console.log(this.props.ItemsChecked);
return (
<label>
<input
type="checkbox"
value={item.id}
checked={isChecked}
onClick={this.handleClick.bind(this)}
/>
{item.name}
</label>
);
}
}
function App() {
return <Items />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Also available as a codesandbox - https://codesandbox.io/s/r44yn2rwm4
Current outstanding issues/extra functionality to be added:
Any help with this would be greatly appreciated, been working on this for a while and exhausted all avenues!
Thanks :)
I have corrected the codesandbox with quite a few changes. It's best if you compare with yours to see all the changes but in summary:
Manage the checkboxes checked state higher up in the Items
componenet instead of a checkbox handling its own state. Pass the state down to it as props and pass it the change event handler as well.
When select all is clicked, you want to put all of your item ids in the checkedListAll
array in the state instead of toggling them.
This is a key change which handles the checkbox change event. When checked, a new array is created with the existing items plus the new item. When unchecked, a new array is created by .filter()
which filters out the item to be removed.
handleCheckboxClick(e) {
const { value, checked } = e.target;
if (checked) {
this.setState(prevState => ({
checkedListAll: [...prevState.checkedListAll, value * 1]
}));
} else {
this.setState(prevState => ({
checkedListAll: prevState.checkedListAll.filter(item => item != value)
}));
}
}
I have following string. var str = "abc, abcd, abc, abcf, abc, abc"; I want to remove abc from a given string with , (space). Basically, I want output something like below. var output = "abcd, abcf"...
I have following string. var str = "abc, abcd, abc, abcf, abc, abc"; I want to remove abc from a given string with , (space). Basically, I want output something like below. var output = "abcd, abcf"...
I got an object which looks like this : { "a": "string not empty", "b": { "c": "string not empty", }, "d": { "e": false, "f": 0, "g": true, ...
I got an object which looks like this : { "a": "string not empty", "b": { "c": "string not empty", }, "d": { "e": false, "f": 0, "g": true, ...
I am following a tutorial in order to perform Asynchronous validation in Angular. What I am trying to achieve is my custom validator which is shouldBeUnique should be call after delay of 2 seconds. I ...
I am following a tutorial in order to perform Asynchronous validation in Angular. What I am trying to achieve is my custom validator which is shouldBeUnique should be call after delay of 2 seconds. I ...
Why jquery parent child selector is not working here. Here article element has it's children element section, and section contains html select tag. So, with parent child logic, it has to work, isn'...
Why jquery parent child selector is not working here. Here article element has it's children element section, and section contains html select tag. So, with parent child logic, it has to work, isn'...