'.push() is improperly pushing twice on Next.js
The .push() in insertToCart() function in Cart component is pushing twice, no idea why. Since all the logs run once, I think it should just work, the appendToCart() works perfectly. I'm returning the previous value on the functions on purpose, to not trigger a re-render of the whole app. Any help would be much appreciated.
Edit : saveItem() is being called twice.
export default function Cart({ children }) {
const [cart, setCart] = useState([]);
useEffect(() => {
setCart(JSON.parse(localStorage.getItem('frentesRosarinos')) || []);
}, []);
function insertToCart(product, amount) {
console.log('insert');
setCart((pc) => {
pc.push({ ...product, amount });
localStorage.setItem('frentesRosarinos', JSON.stringify(pc));
return pc;
});
}
function appendToCart(id, amount) {
console.log('append');
setCart((pc) => {
const savedItem = pc.find((c) => c.id === id);
savedItem.amount = amount;
localStorage.setItem('frentesRosarinos', JSON.stringify(pc));
return pc;
});
}
return (
<CartContext.Provider
value={{ cart, insertToCart, appendToCart }}
>
{children}
</CartContext.Provider>
);
}
export default function Product({ product, isCart }) {
const { cart, appendToCart, insertToCart } =
useContext(CartContext);
const [addItemClosed, setAddItemClosed] = useState(true);
const [amount, setAmount] = useState(
product.amount || cart.find(({ id }) => id === product.id)?.amount || 0,
);
const amountToAdd = useRef(0);
function saveItem(value) {
setAmount((pa) => {
const newAmount = parseInt(value) + pa;
pa === 0 ? insertToCart(product, newAmount) : appendToCart(product.id, newAmount);
return newAmount;
});
amountToAdd.current.value = 0;
}
return (
<div className={styles.container}>
<div className={styles.addItemsContainer}>
<p
onClick={() => setAddItemClosed((ps) => !ps)}
className={`${styles.addItemsButton} ${addItemClosed ? styles.addBorders : ''}`}
>
Agregar más items
</p>
<div
className={`${styles.quantity} ${
addItemClosed ? styles.addBorders : styles.firstChildHide
}`}
>
<span>Ingrese una cantidad</span>
<input ref={amountToAdd} type='number' />
</div>
<p
className={`${styles.addOne} ${
addItemClosed ? styles.addBorders : styles.secondChildHide
}`}
onClick={() => saveItem(amountToAdd.current.value)}
>
Agregar
</p>
</div>
<div style={{ margin: '10px 0' }}>
<PressButton action={() => saveItem(1)} size='medium' lightMode>
{isCart ? 'Agregar otro' : 'Añadir al carrito'}
</PressButton>
</div>
</div>
);
}
Solution 1:[1]
I'm returning the previous value on the functions on purpose, to not trigger a re-render of the whole app. Any help would be much appreciated
You should not do this, official react docs say you should update state in immutable way, and you should follow this advice. Also here:
pc.push({ ...product, amount });
again you are doing mutation which is not OK.
PS. Also in general you should be careful when calling functions inside functional set state in case they perform side effects, because in strict mode the setter function will be invoked twice. Calling a set state within another set state (as you call setCart inside setAmount) could be considered a side effect.
Solution 2:[2]
- You shouldn't be calling inside a state setter a function that will call this same setter. Change
saveItemto this:
function saveItem(value) {
amount === 0 ? insertToCart(product, newAmount) : appendToCart(product.id, newAmount);
setAmount((pa) => {
const newAmount = parseInt(value) + pa;
return newAmount;
});
amountToAdd.current.value = 0;
}
- Change
insertToCart, so you don't mutate the array (this linepc.push({ ...product, amount });is a state mutation). Use destructing instead, like so:
function insertToCart(product, amount) {
console.log('insert');
setCart((pc) => {
const newCart = [...pc, { ...product, amount }]
localStorage.setItem('frentesRosarinos', JSON.stringify(newCart));
return newCart;
});
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
