reactjsjsxselectall

State is not Reflecting after calling HandleCheck Method in React Product List Program


I am trying to build a product list with selectAll functionality but when I'm clicking on selectAll checkBox the state[checkBoxes Array] isn't reflecting the other checkboxes after calling handleCheck() method. I have also used useEffect() with chkAll as a dependency, but the problem didn't resolve. Please help me to resolve this problem

enter image description here

Whole Code...

import React, { useEffect, useState } from 'react'
import { Card,Container, Row, Col,Button,Media,Form,Tooltip } from "react-bootstrap";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSearch,faFilter, faTimes,faEdit } from '@fortawesome/free-solid-svg-icons';
import Checkbox from '../CustomCheckbox/CustomCheckbox';
import './ProductListData.css'

const styles = {
    mediaItem: {
    //   margin:'1rem',
    //   border: "1px solid gray",
      backgroundColor: "#f5f5f5",
 
    },
    mediaItemButtons: {
      paddingTop: "5px",
      paddingBottom: "5px"
    }
  };

export default function P2({products,allCheck,setAllCheck}) {
    var f=0;
    const [state,setState]=useState({
        checkedBoxes:[],
        
    })
    const [chkBox,setChkBox]=useState(false)
    const [q,setQ]=useState("")

    const Search=(products)=>{
        return products.filter(product=>
         product.name.toLowerCase().indexOf(q.toLowerCase())!==-1 || //str.includes(PATTERN)
         product.price.toLowerCase().indexOf(q.toLowerCase())!==-1 ||
         product.manufacturer.toLowerCase().indexOf(q.toLowerCase())!==-1
         );
     
       // return products.filter(product=>product.name.toLowerCase()===q.toLowerCase())
        }

    const handleCheck=(e,product)=>{
        let itemName=e.target.name;
        let checked=e.target.checked;
        if(itemName==='checkAll'){
            if(checked){
            setChkBox(true)
                setState(prevState=>{
                   let  {checkedBoxes}=prevState;

                    let categories = new Set(checkedBoxes)
                    
                    

                    for(var i=0;i<products.length;i++)
                    categories.add(products[i].id)
                    checkedBoxes=Array.from(categories)
                    return {...prevState.checkedBoxes,  checkedBoxes: checkedBoxes }

                })
           console.log("Checked",state.checkedBoxes);
        }
        else{
            setChkBox(false);

           setState(prevState=>{
                let {checkedBoxes}=prevState;
               checkedBoxes.splice(0, checkedBoxes.length)
               return {...prevState.checkedBoxes, checkedBoxes: checkedBoxes };
           });
           console.log("UnChecked",state.checkedBoxes);
        }
        }

        else if(checked) {
            var arr = state.checkedBoxes;
            arr.push(product.id);
            
            setState(state=>({...state,checkedBoxes:arr}));
        } else {            
            let products = state.checkedBoxes.splice(state.checkedBoxes.indexOf(product.id), 1);
            
            setState(state=>({...state,checkedBoxes:products}));
        }   
    }
   

    return (
        <>
          <div className='products'>
                 <Card  className='cardpl'>
                 <Card.Header className='header'>
                     <div className='header-filter'>
                     <input
                          // id={id}
                          name='checkAll'
                          type="checkbox"
                          checked={chkBox}
                          onChange={(e)=>handleCheck(e)}
                          
                      />
                        {/* <Checkbox 
                          number={1000}  
                          isChecked={false} lebal={'Chk'}/> */}

                        <div className='search-bar' >
                          <FontAwesomeIcon icon={faSearch} />

                          <input type="text" value={q} placeholder="Search Products" onChange={(e)=>{setQ(e.target.value)}}/>
                          
                          </div>
                          <div className='filter-icon'>
                          <Button className='filter-button' style={{background:'transparent',color:'#3c44b1',border:'none',boxShadow:'none'}}> <FontAwesomeIcon icon={faFilter} /></Button>
                          </div>
                          {/* <FontAwesomeIcon icon={f}/> */}
                   </div>
                  </Card.Header>
                 <Card.Body className='productListBody overflow-auto custom-scrollbar-css '>



                 
            <p>{products.length!==0?Search(products).map((product,id)=>(
                <div style={{display:'flex',alignItems:'center',border:'1px solid #dee2e6',margin:'.5rem'}}>
             {/* <Checkbox
             number={id}
             isChecked={false}
             /> */}
             
             <input
                id={id}
                type="checkbox"
                name={product.id}
                value={product.id}
                checked={state.checkedBoxes.find((p) => p.id === product.id)}
                onChange={(e) => handleCheck(e, product)}

        />
            <Media key={product.id} 
                    style={{padding:'.5rem',flex:'1'}} className={styles.mediaItem}>
                            <img
                                width={100}
                                height={100}
                                className="align-self-center mr-3"
                                src="https://i5.walmartimages.com/asr/e73e1252-642c-4473-93ea-fd3b564a7027_1.3e81ea58fa3042452fe185129a4a865f.jpeg?odnWidth=undefined&odnHeight=undefined&odnBg=ffffff"
                                alt="Generic placeholder"
                            />
                            
                            <Media.Body className={styles.mediaBody}>
                                <p><b>{product.name}</b></p>
                                <Row  className='product-row'>
                                <Col className='product-col' xs={2}>
                                    <strong>By:</strong>{product.manufacturer}
                                </Col>
                                <Col className='product-col' xs={2}><b>4.5</b>/5</Col>
                                <Col className='product-col' xs={2}>
                                    <strong>${product.price}</strong>
                                </Col>
                                <Col className='product-col' xs={2}><strong>Stock</strong>{product.stock}</Col>
                                <Col xs={2} style={{minWidth:'10rem'}}>
                                    <div className='action-button'>
                                    <Button variant="primary" size="sm">
                                    <FontAwesomeIcon icon={faEdit}/>
                                    </Button>
                                    <Button variant="danger" size="sm">
                                    <FontAwesomeIcon icon={faTimes}/>
                                    </Button>
                                    </div>
                                </Col>
                                </Row>


                               
                            </Media.Body>
                            </Media></div>)):null}
                            
                </p>    
                </Card.Body>   
                           
                         
                           </Card>
                           </div>
        </>
    )
}

Solution

  • Issue

    It seems your checkedBoxes state is an array of ids.

    setState((prevState) => {
      let { checkedBoxes } = prevState;
    
      let categories = new Set(checkedBoxes);
    
      for (var i = 0; i < products.length; i++) {
        categories.add(products[i].id); // <-- product ids into set
      }
      checkedBoxes = Array.from(categories); // <-- set -> array
      return {
        ...prevState.checkedBoxes,
        checkedBoxes: checkedBoxes, // <-- array of ids into state
      };
    });
    

    but your render code is searching this array as if they were objects with an id property.

    checked={state.checkedBoxes.find((p) => p.id === product.id)}
    

    Solution

    Just check if the checkedBoxes array includes the product id

    checked={state.checkedBoxes.includes(product.id)}
    

    Side Note: Console logging state right after an enqueued update (i.e. console.log("Checked",state.checkedBoxes); will only log the state from the current render cycle, not what you are updating it to for the next render cycle. You can use an useEffect hook with a dependency on the chunk of state you want to log after it's updated.

    useEffect(
      () => console.log("Checked", state.checkedBoxes),
      [state.checkedBoxes],
    );