Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Updating local-storage based on mapped components in React

I am trying to create a cart function, where I already have a way to create the cart in local storage with JSON. The problem arises when I try to add a way for the user to change the quantity of each product in the cart.

The handleItemQuantityChange function works, but only after the user has clicked the change quantity buttons twice. This causes the local storage to only update after the function runs twice, therefore when the user refreshes the page after only clicking change once, it will not be updated. Im new to react so bare with me please.

Here is my cart page:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import PrimaryButton from '../components/PrimaryButton';
import './Cart.css';
import React, { useState, useEffect } from 'react';

export default function Cart() {
    const [cartEmpty, setCartEmpty] = useState('');
    const [cart, setCart] = useState(JSON.parse(localStorage.getItem('cart')));
    const initialQuantity = {}; // Object to store previous quantities

    const handleItemQuantityChange = (itemId, newQuantity) => {
        setCart(prevCart =>
            prevCart.map(item => (item.id === itemId ? { ...item, quantity: newQuantity } : item))
        );
        initialQuantity[itemId] = newQuantity; // Update previous quantity
        localStorage.setItem('cart', JSON.stringify(cart));
    };

    useEffect(() => {
        setInterval(() => {
            if (localStorage.getItem('cartAmount') === '0') {
                setCartEmpty('empty');
            } else {
                setCartEmpty('');
            }
        }, 1000);
    }, []);

    return (
        <main id='main' className="cart">
            <h1 className='title'>Cart</h1>
            <section className={`cart-items ${cartEmpty}`}>
                {cartEmpty === 'empty' ?
                    <>
                        <span>No items in cart</span><PrimaryButton
                            to={'/products'}
                            navButton={true}
                            text={'Shop Now'}
                        />
                    </>
                    :
                    <>
                        {cart.map((item) =>
                            <div className='cart-item' key={item.id}>
                                <img
                                    src={item.image}
                                    alt={item.title}
                                />
                                <div className='cart-item-info'>
                                    <h2 className='cart-item-title'>{item.title}</h2>
                                    <div className='cart-item-controls'>
                                        <span className='cart-item-price'>${item.price}</span>
                                        <div className="quantity">
                                            <button
                                                onClick={() => handleItemQuantityChange(item.id, item.quantity - 1)}
                                                aria-label="Decrease quantity"
                                            >-</button>
                                            <input
                                                value={initialQuantity[item.id] || item.quantity}
                                                min={1}
                                                max={100}
                                                onChange={e => {
                                                    const newQuantity = parseInt(e.target.value, 10) || 1;
                                                    handleItemQuantityChange(item.id, newQuantity);
                                                }}
                                                aria-label={"Quantity is " + item.quantity} type="number"
                                                name="quantity"
                                            />
                                            <button
                                                onClick={() => handleItemQuantityChange(item.id, item.quantity + 1)}
                                                aria-label="Increase quantity"
                                            >+</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        )}
                    </>
                }
            </section>
        </main>
    )
};

Im sure that my code has a bunch of redundant pieces so if you want to correct me on them as well that is okay, but don’t feel obligated to. Thanks in advance for anyone that answers.

>Solution :

The problem is with the asynchronous nature of setCart inside the handleItemQuantityChange fn. It is very likely that by the time execution reaches localStorage.setItem, the cart variable isn’t updated yet.

A workaround can be

const handleItemQuantityChange = (itemId, newQuantity) => {
        const newCart = cart.map(item => (item.id === itemId ? { ...item, quantity: newQuantity } : item))
        setCart(newCart);
        initialQuantity[itemId] = newQuantity;
        localStorage.setItem('cart', JSON.stringify(newCart));
    };

The prevCart => .. works when you want to serialize multiple state updates, it doesn’t have an await like effect with synchronous code.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading