I am trying to create a component that will display the products from my database with a checkbox in each of them. Then I should be able to delete all checked items from the database with one press of a button in the header component.
This is the work in progress:
import React, { useState, useEffect } from "react";
import { Container, ItemDescription, ProductContainer } from "./style";
const API = "http://localhost:4000/select";
interface DATA {
sku: string,
name: string,
price: string,
type: string,
size: number,
weight: number,
height: number,
width: number,
length: number,
}
interface IProps {
del: boolean
}
const ProductDisplay = ({del}:IProps) => {
const [productList, setProductList] = useState<Array<DATA>>([]);
var checked = new Array(productList.length).fill(false); // this gives me what I want
const [checkedItems, setCheckedItems] = useState(checked); // here it gives me an empty undefined array
const [deleteList, setDeleteList] = useState<Array<DATA>>([]);
useEffect(() => {
const fetchData = async (data: any) => {
try {
const response = await fetch(data);
const results = await response.json();
setProductList(results);
} catch (e) {
console.log(e);
}
};
fetchData(API);
}, []);
const handleOnChange = (position:number) => {
const updatedCheckedItems = checkedItems.map((item, index) =>
index === position ? !item : item
);
setCheckedItems(updatedCheckedItems);
const skuList = updatedCheckedItems.reduce(
(currentState, index) => {
if (currentState === true) {
return productList[index].sku;
}
return null;
}
);
setDeleteList(skuList);
console.log(checkedItems);
};
return (
<div>
{productList.length === 0 && <h4>Fetching Checkboxes ...</h4>}
{productList.length > 0 &&
<Container>
{productList.map((item: any, index) => (
<ProductContainer>
<input type="checkbox"
value={item.sku}
id={`custom-checkbox-${index}`}
checked={checkedItems[index]}
onChange={() => handleOnChange(index)}
/>
<ItemDescription>
<span className="sku" >{item.sku}</span>
<span className="name" >{item.name}</span>
<span className="price" >{item.price} $</span>
{item.size && <span className="size" >Size: {item.size}MB</span>}
{item.weight && <span className="weight" >Weight: {item.weight}KG</span>}
{item.height && <span className="dimention" >Dimention: {item.height} x {item.width} x {item.length}</span>}
</ItemDescription>
</ProductContainer>
))}
</Container>
}
);
};
export default ProductDisplay;
The product display is working fine, the checked variable shows me the array with 11 false booleans that I want, but the state for checkeItems gives me an empty array. Because of that I haven't been able to test if the rest of the code allows me to produce a list of skus from the checked items for deletion.
I have tried creating the new array as the starting state with the same result:
const [checkedItems, setCheckedItems] = useState(
new Array(productList.length).fill(false)
);
I'm quite new to this and appreciate any help, thanks!
You pass two different parameters to useState(checked)
in two render, which only uses the first one (the empty array) as the initValue.
You should use useEffect
. Just like:
function App() => {
const [productList, setProductList] = useState<Array<DATA>>([]);
const [checkedItems, setCheckedItems] = useState<Array<Boolean>>([]);
useEffect(() => {
const fetchData = async (data: any) => {
try {
const response = await fetch(data);
const results = await response.json();
setProductList(results);
//
const checked = new Array(results.length).fill(false);
setCheckedItems(checked)
} catch (e) {
console.log(e);
}
};
fetchData(API);
}, []);
// ......
}
After react18, setProductList
and setCheckedItems
in useEffect
will only cause one re-render. See Automatic batching for fewer renders in React 18