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
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>
</>
)
}
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)}
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],
);