Chicken Dinner
Chicken Dinner

Reputation: 98

React - Checkbox select all

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!

Upvotes: 3

Views: 16568

Answers (3)

Irfan
Irfan

Reputation: 7059

I have modified @MrCode fiddle to make some more changes as the functionality check/uncheck Select all checkbox based on individual items was missing. So if individual items are checked/unchecked it should reset "Select All" state as well.

if (checked) {
    const collection = this.getAllItems();
    this.setState(prevState => ({
        checkedListAll: [...prevState.checkedListAll, value * 1],
        ItemsChecked: collection.length === prevState.checkedListAll.length + 1
    }));
} else {
    this.setState(prevState => ({
        checkedListAll: prevState.checkedListAll.filter(item => item != value),
        ItemsChecked: false
    }));
}

Working Codesandbox

Upvotes: 3

MrCode
MrCode

Reputation: 64536

Working codesandbox

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)
    }));
  }
}

Upvotes: 5

Hamburguesa
Hamburguesa

Reputation: 27

I would check your Property Types. Based on the CodeSandbox you linked, notice how when you click on Select all and then individually select the check boxes, there looks to be Numbers from the 'Select All' and Strings coming from the Individual checkboxes.

Upvotes: 0

Related Questions