I can't figure out how to get more values to show in my table using REACT. The only thing that I am able to show is the cost. I can't show Manufacturer and Item. So When I choose Iphone12 - Manufacturer will be "Apple" and Item will be "iPhone 12" & Galaxy S21 will be "Android" and "Galaxy 21",from the data structure in index.
This is my Index.js :
import React from "react";
import ReactDOM from "react-dom";
import "./Home.css";
import "bootstrap/dist/css/bootstrap.min.css";
import MTWork from "./MTWork";
let inventory = [
{ id: 0, manufact: "None", item: "None", descr: "None", avail: 0, price: 0 },
{
id: 100,
manufact: "Samsung",
item: "Galaxy S22",
descr: "The Lastest Phone",
avail: 22,
price: 1399.99,
},
{
id: 101,
manufact: "Samsung",
item: "Galaxy S21",
descr: "Recently refurbished like new",
avail: 5,
price: 699.99,
},
{
id: 102,
manufact: "Samsung",
item: "Galaxy S20",
descr: "Great phone works well",
avail: 3,
price: 399.99,
},
{
id: 103,
manufact: "Apple",
item: "iPhone 13",
descr: "New and Shiny, Nothing better",
avail: 13,
price: 1299.99,
},
{
id: 104,
manufact: "Apple",
item: "iPhone 12",
descr: "Refurbished but perfect",
avail: 13,
price: 899.99,
},
{
id: 105,
manufact: "Apple",
item: "iPhone 11",
descr: "Works great!",
avail: 12,
price: 599.99,
},
];
let warranty = [
{ id: 0, plan: "None", cost: 0 },
{ id: 1, plan: "Extended", cost: 0.15 },
{ id: 2, plan: "Normal", cost: 0.1 },
{ id: 3, plan: "Base", cost: 0.05 },
];
let title = "Teds Technology Treasures";
ReactDOM.render(
<React.StrictMode>
<MTWork title={title} phones={inventory} wrnty={warranty} />
</React.StrictMode>,
document.getElementById("root")
);
THIS IS MY MTWork.js
import { useState } from "react";
function MTWork(props) {
const { phones, title, wrnty } = props;
const [name, setName] = useState("Kels");
const [itm, setAndroid] = useState();
const [Atem, setApple] = useState();
const [Warrant, setWarranty] = useState(0);
const [total, setTotal] = useState(0);
function updateName(n) {
setName(n.target.value);
}
function updateAndroid(a) {
setAndroid(a.target.value);
}
function updateApple(ap) {
setApple(ap.target.value);
}
function updateWarranty(w) {
setWarranty(w.target.value);
}
function doOrder() {
let total = 0;
let total2 = parseFloat(itm) + parseFloat(Atem);
let wTotal = total2 * parseFloat(Warrant);
total += total2 + wTotal;
return setTotal(total);
}
return (
<div>
<div className="container-fluid">
<div className="row">
<div className="col">
<h1>
<span className="title">{title}</span>
</h1>
</div>
</div>
<div className="row">
<div className="col-4">
Name: <input type="text" value={name} onChange={updateName} />
<br />
Android Phone:
<select onChange={updateAndroid} value={itm}>
{phones.map((phn) => (
<option key={phn.id} value={phn.price}>
{phn.item}
</option>
))}
</select>
<br />
Apple Phone:
<select onChange={updateApple} value={Atem}>
{phones.map((Apn) => (
<option key={Apn.id} value={Apn.price}>
{Apn.item}
</option>
))}
</select>
<br />
Warranty:
<select onChange={updateWarranty} value={Warrant}>
{wrnty.map((wrn) => (
<option key={wrn.id} value={wrn.cost}>
{wrn.plan}
</option>
))}
</select>
<br />
<button className="btn btn-dark" onClick={() => doOrder()}>
Order
</button>
<h2 style={{ color: "blue" }}>Results For Name: {name}</h2>
<table className="table">
<tbody className="color">
<tr>
<th>Manufacturer:</th>
<th>Item:</th>
<th>Cost:</th>
</tr>
<tr>
<td scope="row">{phones.maunfact}</td>
<td scope="row">{phones.price}</td>
<td scope="row">{itm}</td>
</tr>
<tr>
<td scope="row">{phones.manufact}</td>
<td scope="row">{phones.price}</td>
<td scope="row">{Atem}</td>
</tr>
<tr>
<td>Warranty</td>
<td scope="row">{props.plan}</td>
<td scope="row">{Warrant}%</td>
</tr>
<tr>
<td scope="row">Total </td>
<td scope="row"></td>
<td scope="row">{total} </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
export default MTWork;
You are storing the price in your selection state.
You should instead store the selection by the id (which will be unique).
That way you can always find the item in your phones/inventory array by id and then get any of the properties (manufact, item, price, desc, avail).
function MTWork(props) {
const { phones } = props;
const [selectedId, setSelectedId] = useState(); // store id
function updatePhone(event) {
setSelectedId(event.target.value);
}
const selectedPhone = phones.find((phone) => phone.id == selectedId) || {};
return (
<div>
Phone:
<select onChange={updatePhone} value={selectedId}>
{phones.map((phone) => (
<option key={phone.id} value={phone.id}>
{phone.item}
</option>
))}
</select>
<table>
<tbody>
<tr>
<th>manufact:</th>
<th>item:</th>
<th>price:</th>
<th>desc:</th>
<th>avail:</th>
</tr>
<tr>
<td>{selectedPhone?.manufact}</td>
<td>{selectedPhone?.item}</td>
<td>{selectedPhone?.price}</td>
<td>{selectedPhone?.descr}</td>
<td>{selectedPhone?.avail}</td>
</tr>
</tbody>
</table>
</div>
);
}
const selectedPhone = phones.find((phone) => phone.id == selectedId) || {};
Uses Array.find on the phones array to get the phone that has the same id as selectedId. If it can't find a match it will return undefined, so we assign a value of empty object {} to selectedPhone.
Related
Im getting the warning Warning: Each child in a list should have a unique "key" prop.. My understanding of react was that when you pass to a new component you give it the Id of a prop, which i am doing, yet im still getting this warning. In my code i have some dummy data that is used to set the state of table data i.e
const DumData =
{ id: 1,
UserGroup:[
{
id: "1",
Name: "Dax Johnson",
AddressLine: "82 Mahu road",
Email: 'DaxIng#Gmail.co.nz',
ContactNumber: "02791743",
Zip: '8801',
Notes: 'His dog is swag',
Animals: [
{ id: "1",
PatientID: "23",
AnimalName: 'SwagDog',
Species: "Canine",
Breed: "Dog",
Gender: "Male",
DOB: "",
Vists: [{
id:1 ,
time: 'October 31st 2021'
},
{
id:2 ,
time: 'October 21st 2021'
}]
},
{ id: '2',
AnimalName: 'CoolCat',
Species: "Feline",
Breed: "Cat",
Gender: "Female",
DOB: "",
Vists: [{
id:1 ,
time: 'March 4th 2021'
}]
}
],
},
{
id: "12",
Name: "Willam McDonald",
AddressLine: "2 Wacky Ave",
Email: 'WILLIAM#hotmail.co.nz',
Zip: '8661',
ContactNumber: "033777300",
Notes: 'His cat is cool',
Animals: [
{
id: "1",
PatientID: "23",
AnimalName: "Molder",
Species: "Feline",
Breed: "Cat",
Gender: "Female",
DOB: "2008",
Vists: [{
id:1 ,
time: 'February 4th 2022'
}]
}
],
},
{
id: "3",
Name: "Oscar Issac",
AddressLine: "2 Wacky Ave",
Email: 'Oscar#hotmail.co.nz',
Zip: '7041',
ContactNumber: "0279000",
Notes: 'His cat is cool',
Animals: [
{
id: "1",
PatientID: "23",
AnimalName: "Cool cat",
Species: "Feline",
Breed: "Cat",
Gender: "Female",
DOB: "2008",
Vists: [{
id:1 ,
time: 'June 24th 2021'
}]
}
],
} ]
};
and then later const [tableData, settableData] = useState(DumData);
I create a component table called Hometable where i pass it the tableData and the key id
<div className='Hometable-div'>
<Hometable
data={tableData}
key={tableData.id}
></Hometable>
</div>
and then i map the data so it is displayed in the table in the Hometable component. like so
function Hometable(props) {
var OwnerName;
var Animalname;
var breed;
return (
<div className='table-container'>
<table>
<thead>
<tr>
<th>Owner</th>
<th>Animal Name</th>
<th>Type/Breed</th>
<th>Vist Time</th>
</tr>
</thead>
<tbody>
{props.data.UserGroup.map((person) => (
OwnerName = person.Name,
person.Animals.map((Animal) => (
Animalname = Animal.AnimalName,
breed = Animal.Breed,
Animal.Vists.map((vist) => (
<tr>
<td> <i class="bi bi-person-fill"></i> {OwnerName} </td>
<td> {Animalname}</td>
<td> {breed} </td>
<td> {vist.time} </td>
</tr>
))
))
))}
<tr>
<td className='footer'>
</td>
<td className='footer'>
</td>
<td className='footer'>
</td>
<td className='footer'>
<button className='TableButton'> Page 1</button>
</td>
</tr>
</tbody>
</table>
</div>
);
}
export default Hometable;
I understand i dont use the key value in Hometable so this might be an easy fix if anyone can help me resolve this warning?
Try passing the key here in the code
{props.data.UserGroup.map((person) => (
OwnerName = person.Name,
person.Animals.map((Animal) => (
Animalname = Animal.AnimalName,
breed = Animal.Breed,
Animal.Vists.map((vist, index) => (
// or visit.id if available
<tr key={index}>
<td> <i class="bi bi-person-fill"></i> {OwnerName} </td>
<td> {Animalname}</td>
<td> {breed} </td>
<td> {vist.time} </td>
</tr>
))
))
))}
It's recommended to use keys coming from the data source such as visit.id. Last resort should be using index. For more information you can read here.
Try using this code :
function Hometable(props) {
var OwnerName;
var Animalname;
var breed;
return (
<div className='table-container'>
<table>
<thead>
<tr>
<th>Owner</th>
<th>Animal Name</th>
<th>Type/Breed</th>
<th>Vist Time</th>
</tr>
</thead>
<tbody>
{props.data.UserGroup.map((person) => (
OwnerName = person.Name,
person.Animals.map((Animal) => (
Animalname = Animal.AnimalName,
breed = Animal.Breed,
Animal.Vists.map((vist,index) => (
<tr key={index} >
<td> <i class="bi bi-person-fill"></i> {OwnerName} </td>
<td> {Animalname}</td>
<td> {breed} </td>
<td> {vist.time} </td>
</tr>
))
))
))}
<tr>
<td className='footer'>
</td>
<td className='footer'>
</td>
<td className='footer'>
</td>
<td className='footer'>
<button className='TableButton'> Page 1</button>
</td>
</tr>
</tbody>
</table>
</div>
);
}
export default Hometable;
In my opinion:
const array = [...];
...
const new_array_for_props = array.map(function(arr, index){
<target_comp props_01={arr.props_01} key={index} />
});
sub component(target_comp) unnecessary uses props.key.
Every time you are in a loop, each element like those ones must define a unique key. And it is a good practice
- tr
- ul
- Component
- etc ...
ex:
Users?.map( (user: User) => (
<tr key={user.id}>
<td>{user.name}</td>
<td>{user.birthdate}</td>
<td>
<UserEdit key={user.id} user={user}>Edit</UserEdit>
</td>
...
</tr>
));
I'm using Vue.js 3 and I have a filter here for one array in this case genres, but how to connect with another array for example here for a category? I tried the same way but I got only duplicate
Can https://stackoverflow.com/users/3330981/tauzn you maybe check this if you
have time?
Here is my code
<template>
<div>
<form>
<input type="checkbox" name="drama" v-model="filters.drama"/>
<label for="drama">drama</label>
<input type="checkbox" name="comedy" v-model="filters.comedy"/>
<label for="comedy">comedy</label>
<input type="checkbox" name="horror" v-model="filters.horror"/>
<label for="horror">horror</label>
<input type="checkbox" name="polovno" v-model="filters.new"/>
<label for="new">new</label>
</form>
<div v-for="(movie) in filteredMovies" :key="movie.title">
<div>
{{ movie.title }}
</div>
</div>
</div>
</template>
<script>
export default {
data: ()=>({
filters: {
horror: true,
comedy: true,
drama: true,
new:true,
old:true,
},
movies: [
{ title: 'Movia A', genres: ['horror'], category:['new']},
{ title: 'Movia B', genres: ['comedy'], category:['old']},
{ title: 'Movia C', genres: ['drama'], category:['new']},
{ title: 'Movia D',genres: ['horror'], category:['new']},
]
}),
computed: {
filteredMovies(){
return this.movies.filter( movie => {
let selectedGenres = Object.keys(this.filters).filter(item => this.filters[item] === true)
return movie.genres.some(item => selectedGenres.includes(item))
})
},
filteredMoviesByCategory(){
return this.movies.filter( movie => {
let selectedCategory = Object.keys(this.filters).filter(item => this.filters[item] === true)
return movie.category.some(item => selectedCategory.includes(item))
})
}
},
}
</script>
So I have checkboxes and this works very well for genres filtering but how to connect that to works with another array in this case category? I'm new at Vue.js and this will help me a lot
Here is Gif how everything works now
I found a solution, with other dummy data but works.
Here is my code
<template>
<h5>List of Products</h5>
<h3>Filter</h3>
<button v-on:click="resetOptions">Reset</button>
<button v-on:click="sorting">Sorting</button>
<button v-on:click="sorting2">Sorting2</button>
<select v-model="category">
<option value="Accessories">Accessories</option>
<option value="Laptop">Laptop</option>
<option value="Stationary">Stationary</option>
</select>
<select v-model="gradovi">
<option value="Podgorica">Podgorica</option>
<option value="Niksic">Niksic</option>
<option value="Herceg Novi">Herceg Novi</option>
</select>
<input type="text" v-model="name" placeholder="Filter By Name"/>
<label for="vol">Price (between 0 and 1000):</label>
<input type="range" v-model.trim="range" min="0" max="1000" step="10"/>
<ul>
<li v-for="product in filterProducts" :key="product.name"> Product Name : {{product.name}} - Price : {{product.price}} ({{product.category}})
{{product.gradovi}}
</li>
</ul>
</template>
<script>
export default {
data: ()=> ( {
gradovi:'',
category: '',
name: '',
range: '0',
products: [
{ name: "Keyboard", price: 44, category: 'Accessories', gradovi:'Podgorica'},
{ name: "Mouse", price: 20, category: 'Accessories', gradovi:'Niksic'},
{ name: "Monitor", price: 399, category: 'Accessories', gradovi:'Herceg Novi'},
{ name: "Dell XPS", price: 599, category: 'Laptop', gradovi:'Podgorica'},
{ name: "MacBook Pro", price: 899, category: 'Laptop', gradovi:'Podgorica'},
{ name: "Pencil Box", price: 6, category: 'Stationary', gradovi:'Niksic'},
{ name: "Pen", price: 2, category: 'Stationary', gradovi:'Niksic'},
{ name: "USB Cable", price: 7, category: 'Accessories', gradovi:'Herceg Novi'},
{ name: "Eraser", price: 2, category: 'Stationary', gradovi:'Podgorica'},
{ name: "Highlighter", price: 5, category: 'Stationary', gradovi:'Niksic'}
]
}),
computed: {
filterProducts: function(){
return this.filterProductsByName(this.filterProductsByRange(this.filterProductsByCity(this.filterProductsByCategory(this.products))))
},
},
methods: {
filterProductsByCategory: function(products){
return products.filter(product => !product.category.indexOf(this.category))
},
filterProductsByName: function(products) {
return products.filter(product => !product.name.toLowerCase().indexOf(this.name.toLowerCase()))
},
filterProductsByCity: function(products) {
return products.filter(product => !product.gradovi.indexOf(this.gradovi))
},
filterProductsByRange: function(products){
return products.filter(product => (product.price >= 0 && product.price <= this.range) ? product : '')
},
sorting:function(){
this.products.sort((a,b)=>(a.price > b.price) ? 1 : -1)
},
sorting2:function(){
this.products.sort((a,b)=>(a.price < b.price) ? 1 : -1)
},
resetOptions:function(){
this.category='',
this.gradovi='',
this.name='',
this.range='1000'
},
},
}
</script>
<style>
</style>
I am using React for my app, I am fetching data from an API at http://localhost:8080/api/suppliers/supplier/list
Here is the data structure that I am getting in the Google Chrome console:
0:{
id: 4
supplierFirstname: "Tom"
supplierLastName: "ABC"
supplierTitle: "TomTheSupplier"
accountNumber: 1122234444
address: "111 ADrive, 1234 ST."
companyName: "TheTom Company & Associates"
email: "tomtomjayjay#email.com"
hourlyRate: 29
phoneNumber: 123456789
otherPhoneNumber: 1023456789
paymentTerms: "Credit"
notes: "Some Supplier"
createdAt: null
typeOfGoods: "Supplies"
website: "www.abc_123.com"
products: [{…}]
components:
[
0: {id: 5, name: "AComponent", unit: null, quantity: 0, componentCost: 0, …}
]
}
Here is my code:
class SupplierData {
constructor(props){
super(props);
this.state = {
supplier: [
{
id: 0,
supplierTitle: "Supplier Title",
supplierFirstName: "First Name",
supplierLastName: "Last Name",
accountNumber: 1122234444,
address: "",
companyName: "",
email: "tomtomjayjay#email.com",
hourlyRate: 29,
phoneNumber: 123456789
otherPhoneNumber: 1023456789
paymentTerms: "Credit"
notes: "Some Supplier"
createdAt: null
typeOfGoods: "Supplies"
website: "www.abc_123.com"
products: [{…}]
components:
[
0: {id: 5, name: "AComponent", unit: null, quantity: 0,
componentCost: 0, …}
]
},
],
errorMessage: [],
};
}
ListAllSuppliers = async () =>{
return await axios.get(`http://localhost:8080/api/suppliers/supplier/list`)
.then((response) =>{
let apiResults = response.data;
this.setState({supplier: apiResults});
}).catch((error) =>{
this.setState({errorMessage: this.state.errorMessage.push(error)});
});
}
TableRow = ({id, supplierTitle, supplierFirstName, supplierLastName}) => {
return (
<tr>
<td>
<span className="fw-normal">
{id}
</span>
</td>
<td>
<span className="fw-normal">
{supplierTitle}
</span>
</td>
<td>
<span className="fw-normal">
{supplierFirstName}
</span>
</td>
<td>
<span className="fw-normal">
{supplierLastName}
</span>
</td>
</tr>
);
};
render(){
return(
<table>
<thead>
<tr>
<th className="border-bottom">#ID</th>
<th className="border-bottom">Title</th>
<th className="border-bottom">FirstName</th>
<th className="border-bottom">LastName</th>
</tr>
</thead>
<tbody>
{this.state.supplier.map((t) => this.TableRow(t))}
</tbody>
</table>
);
}
I am getting the following error, here is what it states:
Error: Objects are not valid as a React child (found: object with keys {id, name, productComponents,
quantity, productCost, productPrice, createdAt, status}). If you meant to render a collection of
children, use an array instead.
in span (at SupplierData.js:158)
in td (at SupplierData.js:157)
in tr (at SupplierData.js:78)
in tbody (at SupplierData.js:259)
why is it pointing out such error .
My Question:
What is a possible fix to the above error ?
You forgot to close the tr tag:
return (
<tr>
<td>
<span className="fw-normal">
{id}
</span>
</td>
<td>
<span className="fw-normal">
{supplierTitle}
</span>
</td>
<td>
<span className="fw-normal">
{supplierFirstName}
</span>
</td>
<td>
<span className="fw-normal">
{supplierLastName}
</span>
</td>
</tr>
);
I need to build a table in order to organize some data. I'm using the "onclick" function in order to call a separate function, which is supposed to increment a state variable up by one. My Chrome Devtools isn't giving me any errors but also isn't updating the stock variable. I'm not sure how to get the state to update and display. Here's my source code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cars: [
{
manufacturer: "Toyota",
model: "Rav4",
year: 2008,
stock: 3,
price: 8500
},
{
manufacturer: "Toyota",
model: "Camry",
year: 2009,
stock: 2,
price: 6500
},
{
manufacturer: "Toyota",
model: "Tacoma",
year: 2016,
stock: 1,
price: 22000
},
{
manufacturer: "BMW",
model: "i3",
year: 2012,
stock: 5,
price: 12000
},
]
};
this.renderCar = this.renderRow.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(() => {
return {stock: this.stock + 1}
})
}
renderRow(car, index) {
return (
<tr key={index}>
<td key={car.manufacturer}>{car.manufacturer}</td>
<td key={car.model}>{car.model}</td>
<td key={car.year}>{car.year}</td>
<td key={car.stock}>{car.stock}</td>
<td key={car.price}>${car.price}.00</td>
<td key={index}><input type="button" onClick={car.handleClick} value="Increment" /></td>
</tr>
)
}
render() {
return (
<div>
<table>
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Option</th>
</tr>
</thead>
<tbody>
{this.state.cars.map(this.renderRow)}
</tbody>
</table>
</div>
);
};
}
ReactDOM.render(<App />, document.getElementById("app"))
I'd make a separate component for the row, so that you can easily update that component to increment the stock value in state:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cars: [
{
manufacturer: "Toyota",
model: "Rav4",
year: 2008,
stock: 3,
price: 8500
},
{
manufacturer: "Toyota",
model: "Camry",
year: 2009,
stock: 2,
price: 6500
},
{
manufacturer: "Toyota",
model: "Tacoma",
year: 2016,
stock: 1,
price: 22000
},
{
manufacturer: "BMW",
model: "i3",
year: 2012,
stock: 5,
price: 12000
},
]
};
}
render() {
return (
<div>
<table>
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Year</th>
<th>Stock</th>
<th>Price</th>
<th>Option</th>
</tr>
</thead>
<tbody>
{this.state.cars.map(
(car, i) => <Row car={car} key={i} />
)}
</tbody>
</table>
</div>
);
};
}
const Row = ({ car }) => {
const [stock, setStock] = React.useState(car.stock);
return (
<tr>
<td>{car.manufacturer}</td>
<td>{car.model}</td>
<td>{car.year}</td>
<td>{stock}</td>
<td>${car.price}.00</td>
<td><input type="button" onClick={() => setStock(stock + 1)} value="Increment" /></td>
</tr>
);
}
ReactDOM.render(<App />, document.getElementById("app"))
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id='app'></div>
You could put it all in one component if you had to, but it'd be a bit cumbersome. While rendering, you'd have to keep track of the render index of a row and pass that along to the click handler, then immutably update the stock property in the state array at that index. A separate component is easier.
handleClick(e){
const index = Number(e.currentTarget.value);
this.setState(this.state.cars.map(car, i)=> {
return i === index ? {...car, stock: car.stock + 1}: car
})
}
renderRow(){
....
<input type="button" onClick={this.handleClick} value={index} />
...
}
So I keep getting this error when I click on checkbox and the desired outcome would be once the checkbox is clicked the property of active should change to opposite. Even If I remove the activeHandler function once i click on checkbox I get the same error but now for the initial mapping of products in tbody
const ProductList = props => {
const [products, setProducts] = useState(
[
{
id: 1,
name: 'Product 1',
ean: 242355,
type: 'Food',
weight: 24,
color: 'blue',
active: true,
quantity: 2,
price: 25
},
{
id: 2,
name: 'Product 2',
ean: 57434,
type: 'Food',
weight: 48,
color: 'red',
active: false,
quantity: 5,
price: 12
}
]
);
const activeHandler = productId => {
setProducts(prevState => {
const updatedProducts = prevState.products.map(prod => {
if (prod.id === productId) {
prod.active = !prod.active
}
return prod
})
return {
products: updatedProducts
}
})
}
return (
<div>
<table className="table">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">EAN</th>
<th scope="col">Type</th>
<th scope="col">Weight</th>
<th scope="col">Color</th>
<th scope="col">Active</th>
<th></th>
</tr>
</thead>
<tbody>
{products.map(product => (
<tr key={product.id}>
<td>{product.name}</td>
<td>{product.ean}</td>
<td>{product.type}</td>
<td>{product.weight}</td>
<td>{product.color}</td>
<td>
<input type="checkbox" checked={product.active} onChange={() => activeHandler(product.id)} />
</td>
<td>
<button className="btn btn-secondary mr-1" disabled={product.active}>VIEW</button>
<button className="btn btn-primary mr-1" disabled={product.active}>EDIT</button>
<button className="btn btn-danger" disabled={product.active}>DELETE</button>
</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
Your prevState is the actual array in this case, so you should map over it and also return it as the new state and not an object with a products key:
setProducts(prevState => {
const updatedProducts = prevState.map(prod => {
if (prod.id === productId) {
prod.active = !prod.active
}
return prod
})
return updatedProducts
})