Reputation: 13
I have a shopping cart that is a nested list. I want to check if a certain item is already in the list before adding it again. If it is already in the list I just want to update the quantity. What am I doing wrong here, and how can I implement localstorage here (hypothetically)
Here is the function:
function addToCart(e) {
var updatedCart = [];
if (cart.includes(e.target.innerText)) {
console.log("Duplicate found");
for (var i = 0; i < cart.length; i++) {
if (cart[i][0] == e.target.innerText) {
cart[i][1]++;
updatedCart.push(cart[i]);
i++;
} else {
i++;
}
}
} else {
setCart((cart) => [...cart, [e.target.innerText, 1]]);
}
}
Upvotes: 1
Views: 1460
Reputation: 359
Or basically, just use a Map
. Helps in performance optimisations as well.
Your cart structure would be like this:
{
productId1:quantity1,
productId2:quantity2,
...
..
}
A small snippet :
let map = new Map(state.cart);
let qty = (map.get(id) || 0) + quantity; //if item is not present will just add the item, else update its quantity
if (qty !== 0) {
map.set(id, qty);
} else {
map.delete(id); //else delete the item from cart
}
Upvotes: 0
Reputation: 15530
Based on the line:
setCart((cart) => [...cart, [e.target.innerText, 1]]);
I may assume, that your cart
contains array of arrays where inner arrays have product name and quantity as its first and second items, repsectively.
If I am correct with that assumption, the condition:
if (cart.includes(e.target.innerText))
Is never met, hence corresponding block never executed.
One more thing I may suggest is to use textContent
rather then innerText
(for performance considerations).
So, your addToCart()
function may look something, like:
const addToCart = ({target:{textContent:itemName}}) => {
const cartShallowCopy = [...cart],
matchingItem = cartShallowCopy.find(([cartItemName]) =>
cartItemName == itemName)
matchingItem
? matchingItem[1]++
: cartShallowCopy.push([itemName, 1])
setCart(cartShallowCopy)
}
following is a proof-of-a-concept live-demo:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const App = () => {
const [cart, setCart] = useState([]),
addToCart = ({target:{textContent:itemName}}) => {
const cartShallowCopy = [...cart],
matchingItem = cartShallowCopy.find(([cartItemName]) =>
cartItemName == itemName)
matchingItem
? matchingItem[1]++
: cartShallowCopy.push([itemName, 1])
setCart(cartShallowCopy)
}
return (
<div>
<h4>These are products:</h4>
<ul>
{
['apple', 'banana', 'pear', 'peach'].map(name => (
<li key={name}>
<span
onClick={addToCart}
className="productItem"
>
{name}
</span>
</li>
))
}
</ul>
<h4>This is cart:</h4>
<ul>
{ !!cart.length && cart.map(([name, qty]) => (
<li key={name}>{name}, {qty}</li>
))
}
</ul>
</div>
)
}
render (
<App />,
rootNode
)
.productItem:hover {
cursor: pointer;
background-color: purple;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Upvotes: 1
Reputation: 299
As a suggestion, it is better (time-complexity wise) to store something like your cart in a javascript object or a map. You could store your item names (it would be better to have some sort of unique id) as the keys, and the item amount as the value; so basically something like that:
const cart = {
itemA: 1,
itemB: 2,
itemC: 4
}
That way you could just update the value by copying the old state and overwrite only the value of the searched key if it exists
const cartCopy = {...cart};
if (!cartCopy[itemName]) {
cartCopy[itemName] = 1;
} else {
cartCopy[itemName] += 1;
}
setState(cartCopy);
You should improve that code by yourself and using this snippet just as some quick tip, as to use localStorage it would be not that hard. Especially with this object way of doing that; just remind that localStorage is slow to access, so I would suggest to update your component's state first, and when is updated just add into the localStorage your object with some kind of label. You should use localStorage only to have some kind of persistency of your cart through the sessions, so basically you'll load the state from localStorage only when you mount the app (if the cart is always visible) and update the localStorage when there is some update to the local state of your cart.
Upvotes: 2