I am trying to create a cart layout that displays the number of quantity of each product listed in React.
The data of quantity is received from a redux state that comes like this:
[
{id: '630788107a0ce75d70be5350', quantity: 1, name: 'Nerea Sheppard', price: 176},
{id: '630788b388944200fdf6a6df', quantity: 1, name: 'Hollee Stanley', price: 423}
]
How to get the quantity each of the product?
const card = useSelector((state) => state.addons.cart);
addons.map((addon) => {
return (
<Button
onClick = {
() => {
dispatch(
REMOVE_FROM_CART({
id: addon._id,
})
);
}
} >
REMOVE ITEM
</Button>
<p>
// How to map and to get the quantity of each product from the card array
</p>
<Button onClick = {
() => {
dispatch(
ADD_TO_CART({
id: addon._id,
name: addon.name,
price: addon.price_in_euro_per_day,
})
);
}
} >
ADD ITEM
</Button>
)})
I guess the product-id is part of addon right? So I guess you want to find the product in the product-list which matches the addon._id?
There are serveral ways to do that, a simple, but a not a optimised solution would be"
// How to map and to get the quantity of each product from the card array
{card.find((item) => item.id === addon._id).quantity}
This will render the quantity directly into the html.
A second solution for quick access would be a hash-map and use the id as a key.
const cardMap = card.reduce((map, item) => {
map[item.id] = item;
return map;
}, {})
// In the html part:
<span>Quantity: {cardMap[addon._id].quantity}</span>
card?.find((x) => x.id === addon._id) ?
card?.find((x) => x.id === addon._id)
.quantity :
0
Related
const listItems = items.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
)
Especially what is the difference between item.id and id? and why use : item at the end?
Full code below, Note that the code isn't something I'm working on, it is a tutorial and there is still more to code
import React from "react";
import { useState } from "react";
import { FaTrashAlt } from "react-icons/fa";
const Content = () => {
const [items, setItems] = useState([
{
id: 1,
checked: false,
item: "One half bag of cocoa covered",
},
{
id: 2,
checked: false,
item: "Item 2",
},
{
id: 3,
checked: false,
item: "Item 3",
},
]);
const handleCheck = (id) => {
console.log(`key: ${id}`);
const listItems = items.map((item) =>
item.id === id ? { ...item, checked: !item.checked } : item
);
setItems(listItems);
localStorage.setItem("shoppinglist", JSON.stringify(listItems));
};
return (
<main>
<main>
<ul>
{items.map((item) => (
<li className="item" key={item.id}>
<input
type="checkbox"
onChange={() => handleCheck(item.id)}
checked={item.checked}
/>
<label
style={item.checked ? { color: "red" } : { color: "green" }}
onClick={() => handleCheck(item.id)}
>
{item.item}
</label>
{/* <button>Delete</button> */}
<FaTrashAlt
onClick={() => handleCheck(item.id)}
role="button"
tabIndex="0"
/>
</li>
))}
</ul>
</main>
</main>
);
};
export default Content;
items.map(item => ) is mapping over an array of items, item is the object of the current item being mapped
id is the id that comes from handleCheck function(triggered onClick), which is the id of the clicked item, while item.id is the id of the current item from the array of objects
item.id === id ? { ...item, checked: !item.checked } : item uses the ternary operator which is a shorthand if/else to check if the id of the current item is the same as the id of the clicked item
if it is(true) it adds to the current item object checked: !item.checked, if it isn't(false) it returns the item object as it is
Here this code trying to keep items array immutable,if you change item.checked value it without using
It will modify items array too
{ ...item, checked: !item.checked }
And Id is used to collect only particular item and modifications of it.
The map function iterates through all the items in a list. Each item has an id, which is the list.id variable. The function takes id as a param.
Then if item.id equals the passed in id, a modified item will be returned. If not, the iten is returned unmodified. That's what :item at the end is for.
A few basics to start since I'm not sure where the confusion lies:
map() takes the contents of a list and returns another list / array
based on those concepts. e.g. [1,2,3].map(x => x *2) will return [ 2, 4, 6 ]
The ternary ?: operator takes three args (hence the name) If the
first is true, returns the second, otherwise the third
e.g. true ? 'yes' : 'no' returns 'yes'
The spread operator populates one object with the contents of
another. e.g. If a = { foo: 'Hello' } then
{ ...a, bar: 'World' } means { foo: 'Hello', bar: 'World' }
So your example takes the first list and If the ID matches the checkbox ID being targetted, it returns a new object with the state flipped, otherwise it returns the original object.
The net effect is a list of IDs with the "checked" status flipped on one of the items. Make sense?
How can useEffect detect the change in an array's object's property
without knowing the state array size because items may be added dynamically
in this particular case the "price" property in one of the objects
The array is a state
Just for example if changing the price property useEffect won't invoke, price will be the same next time (after - localStorage.getItem)
(In my app I change it dynamically in a different way this is for example).
const checkUseEffectLocalS = () => {
array[0]['Price'] = '12';
setItemsArray(array);
};
return (
<>
<div>
<button
onClick={() => checkUseEffectLocalS()}>
Check
</button>
</>
);
useEffect(() => {
localStorage.setItem(userItems, JSON.stringify(array));
}, [array.map((item) => item.price)]); //Tried this way also but it didn't worked
Niether
useEffect(() => {
localStorage.setItem(userItems, JSON.stringify(array));
}, [array]); // won't work
The array structure
array([
{
id: 1,
productName: 'Vitamin',
price: '10$',
},
{
id: 2,
productName: 'Powder',
price: '26$',
},
{
id: 3,
productName: 'Multivitamin',
price: '17.5$',
},
]);
Before asking I checked very similar question but with no real answer - stackoverflow
Thanks in advance.
Without using useEffect
const checkUseEffectLocalS = () => {
let arr= [...array]
arr[0]['Price'] = '12';
localStorage.setItem(userItems, JSON.stringify(arr))
setItemsArray(prev=>arr);
};
return (
<>
<div>
<button
onClick={() => checkUseEffectLocalS()}>
Check
</button>
</>
)
By using useEffect
useEffect(() => {
localStorage.setItem(userItems, JSON.stringify(array))
}, [JSON.stringify(array)])
I'm creating this component that will add item into the cart. The output that I want if the user add a duplicate value there's a specific key value name noOfOrder that will be incremented. My array will be stored in localStorage.
LocalStorage
0: {title: "Spicy Ramen",…}
description: "The perfect hybrid of fancy restaurant-style ramen and the pre-packaged instant noodles."
noOfOrders: 1
price: 17
title: "Spicy Ramen"
1: {title: "Spicy Chicken",…}
description: "Sweet and Spicy Chicken is perfectly crispy and coated in the most delicious, sweet, sti"
noOfOrders: 1
price: 4
title: "Spicy Chicken"
UseState
const [checkOutItems, setCheckOutItems] = useState(
JSON.parse(localStorage.getItem('CheckOutItem')) || []
);
AddToCart Function
const handleAddToCart= () => {
let oldArrays = JSON.parse(localStorage.getItem('CheckOutItem')) || [];
let itemArrays = {
title: itemInfo.title,
description: itemInfo.description,
price: itemInfo.price * noOfItem || itemInfo.price,
noOfOrders: 1,
};
if (oldArrays.some((item) => item.title === itemArrays.title)) {
const match = oldArrays.findIndex(
(product) => product.title === itemArrays.title
);
if (match) {
match.noOfOrders++;
}
oldArrays[oldArrays.findIndex((el) => el.title === match.title)] = match;
localStorage.setItem('CheckOutItem', JSON.stringify(oldArrays));
} else {
setCheckOutItems((prev) => [...prev, itemArrays]);
oldArrays.push(itemArrays);
}
localStorage.setItem('CheckOutItem', JSON.stringify(oldArrays));
};
Thanks!
This will mutate the products so make sure it's a clone or if you parsing from localstorage then you don't really need to clone as this object is created
const key = 'cart'
const products = JSON.parse(localStorage.getItem(key)) || [];
const match = products.find(product => product.id === id);
if (match) {
match.count++;
}
localStorage.setItem(key, JSON.stringify(products));
I am working on a project where I have 4 options, but I should only be able to select one option per screen. The option pushes an object with an id and a score (based on the screen) to an array in the state. So if I make one selection and change to another, I'd like to able to remove the previous object and append the new one in an array in the state (for each screen). I have script to append and remove the object if I'm clicking on the same option, but I'm not sure what I would do to replace the object with a new one if I make a selection then immediately go to another.
The array looks as follows:
scores: [
[{id: "G1", score: "4"}, {id: "B1", score: "3"}, {id: "O1", score: "2"}, {id: "R1", score: "1"}],
[{id: "B2", score: "4"}, {id: "G2", score: "3"}, {id: "R2", score: "1"}, {id: "O2", score: "4"}]
]
Here is the code I have now:
handleScore = (id, score, idx) => {
const {
statements,
scores
} = this.state;
if (statements[0][idx].isSelected) {
this.setState({
scores: scores.map((item, index) => index === 0 ? [...item, {
'id': id,
'score': score
}] : item)
});
} else {
this.setState({
scores: scores.map((item, index) => index === 0 ? item.filter(i => i.id !== item[0].id) : item)
});
}
}
and in the render method I have:
<div
className={`statement ${(this.state.statements[0][index].isSelected) ? 'selected' : ''}`} key={item.id} onClick={e => {
e.preventDefault();
this.handleScore(item.id, match.params.score, index)
}}>
{item.statement}
</div>
Thank you in advance!
I don't know what you are trying to do, but you can use the splice() method to remove an item from an array. Check this repro on Stackblitz to see the result, and here is the code in case it does not works:
import React, { Component } from "react";
import { render } from "react-dom";
import Hello from "./Hello";
import "./style.css";
const App = () => {
const [data, setData] = React.useState([
{id: 0, score:10},
{id: 1, score:20},
{id: 2, score:30},
{id: 3, score:40},
]);
const displayItems = () => {
return data.map((item, index) => {
return (
<div>
<span>id: {item.id}, score: {item.score}</span>{' '}
<button onClick={() => replaceItem(item, index)}>Replace item</button>
</div>
)});
}
const replaceItem = (item, index) => {
const newItem = {id:4, score: 50};
let newData = [...data];
// -----Replace ONE ('1') item at specific index by the newItem
// newData.splice(index, 1, newItem);
// -----OR remove an item and place another at the first index
newData.splice(index, 1); // remove item from array
newData.unshift(item); // Put the item at the first index of the array
//------
setData(newData); // set your newData array as the new state
}
return (
<div>
{displayItems()}
</div>
);
};
render(<App />, document.getElementById("root"));
I placed two use case of splice so you can coment/uncomment to see the result.
I am learning react and came across a tutorial where a delete button is added in the JSX and a function has been defined to execute the event. However, I am confused what does the code below do.
const deleteList=this.state.list.filter(item=>item.objectID!==id);
In particular I am confused what does the below logic mean:
(item=>item.objectID!==id);
this.state.list.filter(item=>item.objectID!==id);
This part loops over all items in list, and returns a new array containing only items that match the condition item.objectID!==id
This is a common syntax to delete one element of a list.
See documentation of filter method.
It is similar to
function(item) {
return item.objectID!==id
}
Basically filter out all elements where id is not equal to item.objectID
For Better understanding I am explaining it with an example.
see the below code:-
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{ id: '1', age: 42 },
{ id: '2', age: 33 },
{ id: '3', age: 68 },
],
};
}
onRemoveItem = id => {
this.setState(state => {
const list = state.list.filter(item => item.objectID !== id);
return {
list,
};
});
};
render() {
return (
<div>
<ul>
{this.state.list.map(item => (
<li key={item.objectID}>
The person is {item.age} years old.
<button
type="button"
onClick={() => this.onRemoveItem(item.objectID)}
>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
}
export default App;
Above code we have React state array of objects (i.e. objectid and age).
while defining onRemoveItem methods id is the parameter.
when onRemoveItem methods is called on the button click event an item.objectID is passed as parameter.
onRemoveItem = id => {
this.setState(state => {
const list = state.list.filter(item => item.objectID !== id);
Here we filter the item from the React state array whose objectid is id and id is nothing but the objectid which is passed as a parameter on the onRemoveItem method calling.