I have a rookie problem, and that is adding the sum total from an object (Cart) in my cart page before I go check out.
Every time I navigate from one screen to the other the amount keeps going up
I want the total amount to be 159, or the correct amount if i add more products
SourceCode
Try to replace this piece of code:
this.cartItems.forEach((value, index) => {
this.totalAmount += parseInt(value.amount);
});
with this:
this.totalAmount = this.cartItems.reduce((acc, item) => {
return acc += item.amount;
}, 0);
In the first case you add a new value to already existing value. And in the reduce version it should rewrite the totalAmount.
Complete working example find out here in this StackBlitz Link
You just need to calculate cart amount using reduce() array function.
this.total = this.cart.reduce( (acc,curVal) => {
return acc + (curVal.amount * curVal.quantity);
//this.temp.push( curVal.amount * curVal.quantity);
},0)
Related
I have an array with nested objects that looks like the one below.
What I'd like to do is loop through it calculate the sum of each item per date.
For example pc + screen = ?
I cannot seem to figure out how to do it properly. I have found this solution, it works great in console.log() but I cannot figure out how to output the result in a div. Should I use a map function ?
const amountPerDate = data.forEach(function (i) {
const sum = i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0);
console.log("the total sum is " + sum);
});
The array:
The code you have posted doesn't seem quite right since forEach won't return anything, and the inner variable sum is not actually available for React to render since it is not in scope (in JavaScript, variables can not escape their containing function, which is function (i) { -- nothing outside of that function can see it).
You were roughly on the right tracks with needing map since that will return an array that represents an accumulation of the return values in the nested callback.
const amountsPerDate = data.map((i) => {
return i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0);
});
amountsPerDate will now be an array of the sums. However, in this process, youve lost the info about which sum correlates to which date. So we need more. We can modify to return both the sum alongside the date (an array of objects, each with a sum and date inside).
const amountsPerDate = data.map((i) => {
return {
sum: i.item.reduce(function (sum, elem) {
return sum + elem.price;
}, 0),
date: i.date
});
Now, you should have something in amountsPerDate that looks like this:
[
{ date: '01/01/2022', sum: 200 },
{ date: '02/01/2022', sum: 30},
]
To display in your react component, it's just a case of rendering it, which will require you to map over this new data and return an element for each entry. You haven't posted your full component, but it will be something like this in your JSX:
<div>
{amountsPerDate.map(sum =>
<div>Date: {sum.date}. Total: {sum.sum}</div>
)}
</div>
Of course you can play with this and move it around as you see fit so it fits however you want it laid out.
It's really worth your time understanding map and the differences with foreach since it's so ubiquitous in functional programming. Foreach and map both loop over each item. But map allows you to return a value within the loop callback, and that value goes on to be part of a new array returned from map that represents that item. You can think of it as a transformation from one array to another -- both with the same length -- but with each item replaced with something of your choosing, calculated from each items original contents.
I am trying to make an addition of a grocery quantity in my react native app. The groceries are coming from firebase firestore with pre-defined prices, I want that when I increase the quantity, the total count of the cart_items should be correctly calculated. I am approaching this by adding it directly from the server.
The issue is, I need to be able to get only the initial price of a grocery, so I can add and subtract at will, instead, I am getting the updated price when I add the quantity, and that updated price is being added to the current price when I need to increase the quantity again. I hope you get what I mean.
const increment = async (id) => {
const itemRef = doc(db, "cartItems", id);
await getDoc(itemRef).then(async (snapshot) => {
// This Line of code is supposed to capture the initial value of the price
let price = snapshot.data().data.price;
console.log(price);
// This Line of code is supposed to capture the initial value of the price
await updateDoc(itemRef, {
quantity: snapshot.data().quantity + 1,
data: {
...snapshot.data().data,
price: snapshot.data().data.price + price,
// I am supposed to use that initial value for this calculation
},
});
});
};
And here's for decreasing the quantity
const decrement = async (id) => {
const itemRef = doc(db, "cartItems", id);
await getDoc(itemRef).then(async (snapshot) => {
// This Line of code is supposed to capture the initial value of the price
let price = snapshot.data().data.price;
console.log(price);
// This Line of code is supposed to capture the initial value of the price
await updateDoc(itemRef, {
quantity:
snapshot.data().quantity === 1 ? 1 : snapshot.data().quantity - 1,
data: {
...snapshot.data().data,
price:
snapshot.data().data.price === price
? price
: snapshot.data().data.price - price,
// I am supposed to use that initial value for this calculation
},
});
});
};
So I just need to know if there's a way I can get only the initial value of the price and not the updated value. Please let me know if I need to clarify anything about the question. It's a really pressing issue for me right now.
I got my answer guys.
I just had to add an initial value that remains constant and doesn't change to my database. That's what I use to make the necessary calculations on my app.
screeen record of the issue: https://streamable.com/ofn42v
it is working fine in local but once deployed to production(vercel), it is not working. i have tried sooo many different things like having a separate state in cart, useEffect with totalQuantity in dependency array and nothing seems to work. Ideally when the totalQuantity inside the context is updated, the components using it should rerender as mentioned in react doc which is happening from n to 2 except for 1. can someone please help :(
my code for the cart icon in nav bar:
function Cart(props) {
const { enableCart, totalQuantity } = useContext(AppContext);
return (
<>
{enableCart ? (
<Link href="/cart" passHref>
<a aria-label="Shopping cart" title="Shopping cart">
<Badge count={totalQuantity} offset={[0, 5]}>
<ShoppingCartIcon className="w-7 h-7" />
</Badge>
</a>
</Link>
) : null}
</>
);
}
Update quantity - code in appContext:
import { useCookies } from "react-cookie";
export const AppProvider = (props) => {
const [cartItems, updateCart] = useState([]);
const [totalQuantity, setTotalQuantity] = useState(0);
const [cookies, setCookie] = useCookies(["cart"]);
const cookieCart = cookies.cart;
useEffect(() => {
cartOperations();
}, []);
const calculateAmountQuantity = (items) => {
let totalCount = 0;
let totalPrice = 0;
items.forEach((item) => {
totalCount += item.quantity;
totalPrice += item.price * item.quantity;
setTotalAmount(totalPrice);
setTotalQuantity(totalCount);
});
};
const cartOperations = async (items) => {
if (items !== undefined) {
updateCart([...items]);
calculateAmountQuantity(items);
} else if (cookieCart !== undefined) {
updateCart([...cookieCart]);
calculateAmountQuantity(cookieCart);
} else {
updateCart([]);
setTotalAmount(0);
setTotalQuantity(0);
}
};
const addItem = (item) => {
let items = cartItems;
let existingItem;
if (items) existingItem = items.find((i) => i.id === item.id);
if (!existingItem) {
items = [
...(items || []),
Object.assign({}, item, {
quantity: 1,
}),
];
updateCart([...items]);
setTotalAmount(totalAmount + item.price * 1);
setTotalQuantity(totalQuantity + 1);
} else {
const index = items.findIndex((i) => i.id === item.id);
items[index] = Object.assign({}, item, {
quantity: existingItem.quantity + 1,
});
updateCart([...items]);
setTotalAmount(totalAmount + existingItem.price);
setTotalQuantity(totalQuantity + 1);
}
saveCartToCookie(items);
saveCartToStrapi(items);
};
i am storing the cart content in cookie.
code for AppContext is here in github, full nav bar code
Live url: https://sunfabb.com
Goto Products, add few items to cart, then try removing one by one from the cart page. (i have enabled react profiler in prod as well)
EDIT: This issue is completely specific to antd library. I was able to debug further based on the below 2 answers and there is nothing wrong with react context or re-render. i tried using a custom badge for cart and it is working perfectly fine. Yet to fix the antd issue though. I can go with custom one, but antd's badge is better with some animations.
As pointed out by #hackape, when setting the value of state to something that depends on the previous value of that state, you should pass a function to the setState instead of a value.
So instead of setTotalQuantity(totalQuantity + 1);, you should say setTotalQuantity(previousQuantity => previousQuantity + 1);.
This is the safe way of doing that, so for example if we are trying to do it twice simultaneously, they both get taken into account, instead of both using the same initial totalQuantity.
Other thing that I would think about changing is that you are setting those quantities and amounts in multiple places, and relying on the previous value. So if it goes out of sync once, it's out of sync also on the next action, and so on.
You could use the useEffect hook for this. Every time the cartItems change, calculate those values again, and do that based only on the new cartItems array, not on the old values.
Something like this for example:
useEffect(() => {
setTotalAmount(cartItems.reduce((total, currentItem) => total + (currentItem.price * currentItem.quantity), 0));
setTotalQuantity(cartItems.reduce((total, currentItem) => total + currentItem.quantity, 0));
}, [cartItems]);
Or if you prefer calling it like you do now, I would still replace the value with the reduce from my example, so it get's calculated based on the whole cart instead of previous value.
A shopping cart is usually something that contains less than 100 entries, so there is really no need to worry about the performance.
From looking at the renders and from seeing that after a refresh the cart shows as empty as should be, it's probably a lifecycle issue.
I'd suggest creating another useEffect hook that listens to totalQuantity or totalAmount (logically the bigger of the two though by the state values it looks either should be fine) and in the hook call change the cart icon based on the updated sum
EDIT:
misread your inter-component imports, because Cart (from components/index/nav.js) should listen for changes from the context.provider you would use a context.consumer on Cart with the totalQuantity value (not just with importing the variable from the context as that rides on the application rendering from other reasons)
see example in consumer docs and in this thread, and check this GitHub issues page for other's detailed journey while encountering this issue more directly
So, I'm attempting to reduce a returned graphql object (See attached image) as follows:
var quantity = itemDetails.reduce((a, itemvariants) => a + itemvariants.quantity, 0);
I get the above mentioned error message. What am I overlooking here?
You're calling reduce on the object, not the itemVariants array.
quantity = itemDetails.itemVariants.reduce((a, variant) => a + variant.quantity, 0);
looks like you want to reduce the items from itemVariants, not itemDetails
let quantity = itemDetails.itemVariants.reduce((total, variant) => total + variant.quantity, 0)
I have the following data-set:
const data = [
["opens", "opens", "opens"], //opens of stock prices
["closes", "closes", "closes"], //closes of stock prices
["highs", "highs", "highs"], //highs of stock prices
["lows", "lows", "lows"] //close of stock prices
];
I have a forEach function that calls another function with the input of res. I am trying to create a rollingInput to feed to my neural network. Right now its only accepting one candle of input for each output. I want to create a rolling input of for this example 5.
this.opens.forEach((x, index) => {
let res = data.reduce((acc, list, idx, arr) => {
if (idx >= list.length)
return acc;
return [...acc, ...arr.map(x => x[idx])];
}, []);
console.log(res);
this.function(res);
});
How can I make sure that when the forEach loop is going that res is actually a sliding window with the newest array being fed last?
I am looking for an output like such
[opens[-5], closes[-5], highs[-5], low[-5],opens[-4], closes[-4], highs[-4], low[-4],opens[-3], closes[-3], highs[-3], low[-3], opens[-3], closes[-2], highs[-2], low[-2], opens[-2], opens, [-1], closes[-1], highs[-1], low[-1], opens[0], closes[0], highs[0], low[0]]
then the next output should start at [0] and end at [5]
not sure if that is the proper way to do it and also not sure how I would cut off the negative values as obviously -5 doesnt exist.
You have arr.map(x => x[idx]) which I assume corresponds to data["opens"].map(x => x[idx])
Instead of data["opens"].map(x => x[idx]) use the index value of map to get all values:
data["opens"].map((value, index)=>{
return [data["opens"][index], data["closes"][index], data["Highs"][index], data["lows"][index]]
})