I'm making a shopping cart and want to show the quantity of all items in the header of my website.
I use useReducer and context to store my items through my applicationand this works. But when I refresh everything is gone. So now I wanna store my quantity in the localStorage.
So if I add a new item to the card I check if quantity already excists in localStorage and add my new quantity to it. But somehow everything runs twice resulting in adding the double amount of intended.
sorry for my grammar english is not my first language
import { useReducer } from "react";
import CartContext from "./cart-context";
const defaultCartState = {
items: [],
totalAmount: 0,
totalQuantity: 0,
};
const cartReducer = (state, action) => {
if (action.type === "ADD") {
const updatedTotalAmount =
state.totalAmount + action.item.price * action.item.amount;
if (JSON.parse(localStorage.getItem("quantity"))) {
state.totalQuantity = JSON.parse(localStorage.getItem("quantity"));
}
console.log(state.totalQuantity);
console.log(action.item.amount);
const updatedTotalQuantity = state.totalQuantity + action.item.amount;
const existingCartItemIndex = state.items.findIndex(
(item) => item.id === action.item.id
);
const existingCartItem = state.items[existingCartItemIndex];
let updatedItems;
if (existingCartItem) {
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount,
};
updatedItems = [...state.items];
updatedItems[existingCartItemIndex] = updatedItem;
} else {
updatedItems = state.items.concat(action.item);
}
localStorage.setItem("quantity", JSON.stringify(updatedTotalQuantity));
return {
items: updatedItems,
totalAmount: updatedTotalAmount,
totalQuantity: updatedTotalQuantity,
};
}
return defaultCartState;
};
CartProvider
const CartProvider = (props) => {
const [cartState, dispatchCartAction] = useReducer(
cartReducer,
defaultCartState
);
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: "ADD", item: item });
};
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: "REMOVE", id: id });
};
const cartContext = {
items: cartState.items,
totalAmount: cartState.totalAmount,
totalQuantity: cartState.totalQuantity,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
};
return (
<CartContext.Provider value={cartContext}>
{props.children}
</CartContext.Provider>
);
};
export default CartProvider;
The console where you see it running twice: Screenshot of console
try to set the value from localstorage only once when you create a context and pass a default state to it like this:
import { useContext, useReducer, createContext } from 'react'
const defaultCartState = {
items: [],
totalAmount: 0,
totalQuantity: JSON.parse(localStorage.getItem('quantity')) ?? 0,
}
const CartContext = createContext(defaultCartState)
const cartReducer = (state, action) => {
if (action.type === 'ADD') {
const updatedTotalAmount = state.totalAmount + action.item.price * action.item.amount
const updatedTotalQuantity = state.totalQuantity + action.item.amount
const existingCartItemIndex = state.items.findIndex((item) => item.id === action.item.id)
const existingCartItem = state.items[existingCartItemIndex]
let updatedItems
if (existingCartItem) {
const updatedItem = {
...existingCartItem,
amount: existingCartItem.amount + action.item.amount,
}
updatedItems = [...state.items]
updatedItems[existingCartItemIndex] = updatedItem
} else {
updatedItems = state.items.concat(action.item)
}
localStorage.setItem('quantity', JSON.stringify(updatedTotalQuantity))
return {
items: updatedItems,
totalAmount: updatedTotalAmount,
totalQuantity: updatedTotalQuantity,
}
}
if (action.type === 'REMOVE') {
const existingCartItem = state.items.find((item) => item.id === action.id)
console.log('stas', action.id, existingCartItem)
const updatedTotalAmount = state.totalAmount - existingCartItem.price * existingCartItem.amount
const updatedTotalQuantity = state.totalQuantity - existingCartItem.amount
const updatedItems = state.items.filter((item) => item.id !== action.id)
localStorage.setItem('quantity', JSON.stringify(updatedTotalQuantity))
return {
items: updatedItems,
totalAmount: updatedTotalAmount,
totalQuantity: updatedTotalQuantity,
}
}
return defaultCartState
}
const CartProvider = (props) => {
const [cartState, dispatchCartAction] = useReducer(cartReducer, defaultCartState)
const addItemToCartHandler = (item) => {
dispatchCartAction({ type: 'ADD', item: item })
}
const removeItemFromCartHandler = (id) => {
dispatchCartAction({ type: 'REMOVE', id: id })
}
const cartContext = {
items: cartState.items,
totalAmount: cartState.totalAmount,
totalQuantity: cartState.totalQuantity,
addItem: addItemToCartHandler,
removeItem: removeItemFromCartHandler,
}
// eslint-disable-next-line react/prop-types
return <CartContext.Provider value={cartContext}>{props.children}</CartContext.Provider>
}
const My = () => {
const { addItem, items, removeItem, totalAmount, totalQuantity } = useContext(CartContext)
console.log('context', addItem, items, removeItem, totalAmount, totalQuantity)
return (
<div>
<div>
items:{' '}
{items.map((item, i) => (
<div key={i}>{JSON.stringify(item)}</div>
))}
</div>
<div>totalAmount: {totalAmount}</div>
<div>totalQuantity: {totalQuantity}</div>
<button onClick={() => addItem({ price: 3, amount: 1, id: 2 })}>add</button>
<button onClick={() => removeItem(2)}>remove</button>
</div>
)
}
const App = () => {
return (
<CartProvider>
<My />
</CartProvider>
)
}
export default App