PandaMastr
PandaMastr

Reputation: 695

Select or unselect check boxes - ReactJS

I have managed to create my function to select a single or multiple boxes. On the single ones I don't have any issue, I mean when you click a box it is checked or unchecked.

But it is not the same case with the select all. Any ideas how can I fix that? (I am using material-ui library for my boxes, but basically they are simple HTML input)

Select all component and function:

<Checkbox
    name="checkboxes"
    checked={this.state.allCheckboxes}
    onChange={this.handleAllCheckboxes}
    indeterminate
    />Select All

Function:

handleAllCheckboxes = (e) => {
    let responseObj = this.state.items;
    let prepareObj = {};
    if(e.target.checked){
        //to do add loop to check all check box
        responseObj.forEach(function(item){
            if(item.documentId !== null && item.documetNumber !== null ){
                prepareObj[item.documentId] = item.documentNumber;
                // this.refs.documentId
            }
        });
        let toSee = Object.keys(prepareObj).length > 0 ? true : false;
        this.setState({
            docList: prepareObj,
            visible: toSee
        })
        let checkboxes = document.getElementsByName('DocCheckbox')
        checkboxes.forEach(function(checkbox){
            checkbox.checked = checkbox.checked
            })
            console.log(checkboxes)
    } else {
        //to do add loop to uncheck all check box
        this.setState({
            prepareObj: {}
        })
    }

    console.log(prepareObj);

};

Select single component and function:

<Checkbox
      name='DocCheckbox'
      type='checkbox'
      color='default'
      value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
      onClick={this.handleCheckboxClick}/>

Function:

handleCheckboxClick = (e, id) => {

    if (id) {


    } else {
        let parsedVal = JSON.parse(e.target.value);
        // console.log(e.target.value)
        let newDocList = { ...this.state.docList };
        if (e.target.checked) {
            this.setState({
                singleCheckbox:true
            })
            newDocList[parsedVal.documentId] = parsedVal.documentNumber;
        } else {
            delete newDocList[parsedVal.documentId];
            this.setState({
                singleCheckbox:false
            })
        }
        let toSee = Object.keys(newDocList).length > 0 ? true : false;
        this.setState(
            {
                docList: newDocList,
                visible: toSee
            },
            () => {
                console.log(this.state.docList);
            }
        );

    }
};

UPDATE Answer of my code based to the reply of @norbitrial ( The answer is based on my properties , calls and data so feel free to modify it for your purpose )

Step 1 - Create a constructor to maintain your data and global checked state

 constructor(props) {
        super(props);
        this.state = { 
            docList: {},
            checked: false,
            hasToCheckAll: false
       }
    }

Step 2 - Create functions to handle single and multiple checkboxes

Handle single checkbox

   handleCheckboxClick = (clickedItem) => {
        console.log(clickedItem)
        // let parsedVal = JSON.parse(e.target.value);
        let newDocList = { ...this.state.docList };
        if (!clickedItem.checked) {
            newDocList[clickedItem.documentId] = clickedItem.documentNumber;
            console.log(newDocList)
        } else {
            delete newDocList[clickedItem.documentId];
        }
        let toSee = Object.keys(newDocList).length > 0 ? true : false;
        console.log(toSee)
        this.setState(
            {
                docList: newDocList,
                visible: toSee
            }, ()=>{
                console.log(newDocList)
            });

        const updatedArray = this.state.items.map((item) => {
            item.checked = item.documentId === clickedItem.documentId ? !item.checked : item.checked;
            return item;
        });

        this.setState({

            items: updatedArray,
        });
    };

Handle all checkboxes

  handleAllCheckboxes = (e) => {
        const hasToCheckAll = !this.state.hasToCheckAll;
        const updatedArray = this.state.items.map((item) => {
            item.checked = hasToCheckAll;
            return item;
        });
        console.log(updatedArray)

        let responseObj = this.state.items;
        let prepareObj = {};
        if (e.target.checked) {
            //to do add loop to check all check box
            responseObj.forEach(function (item) {
                if (item.documentId !== null && item.documetNumber !== null) {
                    prepareObj[item.documentId] = item.documentNumber;

                }
            });
            let toSee = Object.keys(prepareObj).length > 0 ? true : false;
            console.log(toSee)
            this.setState({
                docList: prepareObj,
                // allCheckboxes: true,
                items: updatedArray,
                hasToCheckAll,
                visible: toSee
            })
            let checkboxes = document.getElementsByName('checkAll')
            checkboxes.forEach(function (checkbox) {
                checkbox.checked = e.target.checked

            })
            console.log(checkboxes)
        } else {
            console.log(updatedArray)
            this.setState({
                docList: {},
                hasToCheckAll:false
            })

        }
    };

Step 3 - Insert the state of the checked boxes inside into the object . And loop over them ( again this is coming from the response from my Back End so for everyone this step will be different )

  .then((response) => {
                // handle success

                let dataItem = response.data.bills;
                let prepareDataItem = [];
                dataItem.forEach(function (item) {
                    item.checked = false;
                    prepareDataItem.push(item);
                })

Step 4 - Render the checkboxes (these checkboxes are based on material-ui library , but it can work for a simple inputs also)

 <Checkbox
       name='DocCheckBox'
       type='checkbox'
       checked={rowData.checked}
       color='default'
       value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
       onChange={() => this.handleCheckboxClick(rowData)}/>


   <Checkbox
        name="checkboxes"
        checked={this.state.hasToCheckAll}
        onChange={this.handleAllCheckboxes}
        indeterminate
        />Select All

Upvotes: 4

Views: 4332

Answers (1)

norbitrial
norbitrial

Reputation: 15166

I would change a bit how you are handling these things in the application.

Technically you are manipulating the DOM directly, instead of leaving it to React with its states. In this case there is a definitely better way to handle checkbox states in the UI.

The solution:

1. Changed default state:

Let me state that I don't have the real data structure what you have so I have the following in the constructor just for the representation:

constructor(props:any) {
    super(props);
    this.state = {
        items: [
            { documentId: 1, documentNumber: 1234, checked: false },
            { documentId: 2, documentNumber: 1235, checked: false },
            { documentId: 3, documentNumber: 1236, checked: false },
        ],
        hasToCheckAll: false,
    }
}

As you can see the items have checked property in order to handle them in the state.

2. Rendering the checkboxes differently

In the render function I have changed couple of things:

render() {
    return (
        <div>
            <Checkbox
                name="checkboxes"
                checked={this.state.hasToCheckAll}
                onChange={() => this.handleAllCheckboxes()}
                indeterminate
                />Select All

            {this.state.items.map((item:any) => {
                return <div key={item.documentNumber}>
                    <Checkbox
                        name="DocCheckbox"
                        color="default"
                        checked={item.checked}
                        value={JSON.stringify({ ...item.documentId, ...item.documentNumber })}
                        onChange={() => this.handleCheckboxClick(item)}/> {item.documentNumber}
                </div>
            })}
    </div>
    )
}

3. Handling checkbox state is also changed:

Take a look at the following handlers:

handleAllCheckboxes = () => {
    const hasToCheckAll = !this.state.hasToCheckAll;
    const updatedArray = this.state.items.map((item:any) => {
        item.checked = hasToCheckAll;
        return item;
    });

    this.setState({
        ...this.state,
        items: updatedArray,
        hasToCheckAll: hasToCheckAll,
    });
};

handleCheckboxClick = (clickedItem:any) => {
    const updatedArray = this.state.items.map((item:any) => {
        item.checked = item.documentId === clickedItem.documentId ? !item.checked : item.checked;
        return item;
    });

    this.setState({
        ...this.state,
        items: updatedArray,
    });
};

The result:

result-gif

Of course you can extend this example with your data manipulation if there is a further need. The solution has been built with TypeScript - I had a project opened with that - but you can remove types where it has been added.

The above example works like charm, I hope this helps!

Upvotes: 1

Related Questions