Randy_Johnson's avatar

Reactjs useState not updating imediately?!

When adding an item to the cart and doing some calculations, I output the result, the first click shows the local array but the useState array doesn't appear until add the next item and seems to be always one pace behind.

useEffect(() => {

        // Calculates the amount of one item in the cart for each item
        let c;
        let arr = [];
        items.forEach(x => {
            c = 0
            items.forEach(y => {
                if (x.id === y.id) {
                    c++
                }
            });
            arr.push({ id: x.id, title: x.title, price: (x.price * c), amount: c })
        });
        let uniqueItems = arr.reduce((acc, item) => {
            if (!(item.id in acc)) {
                acc[item.id] = item;
            }
            return acc;
        }, {});

		setItemsCount(uniqueItems)
        console.log(uniqueItems)
        console.log(itemsCount)
  
        // Calculates the total cost of each product
        // Could be done in the above code making this redundent. 
        const sumWithInitial = items.reduce((accumulator, object) => {
            return accumulator + object.price;
        }, 0);
        setTotal(sumWithInitial);

    }, [items]);
0 likes
10 replies
Randy_Johnson's avatar

@MohamedTammam This isn't the problem, when clicking an button, the useEffect is run because the cart has been updated, uniqueItems displays the items, on that click, but itemsCount setItemsCount(uniqueItems) doesn't display until the button is pressed again.

I tried what you suggested but it has the same outcome.

Randy_Johnson's avatar

omg. Sorry, I just console logged it in the return of the component and everything is working as should.

But now I have another problem, in that the useState doesn't want map through. Its displaying the error of Uncaught TypeError: itemsCount.map is not a function.

{items.length === 0 ? (
                        <p className="text-white text-center">The cart is empty!</p>
                    ) : (
                        
                            itemsCount.map((item, index) => (
                                <li key={index}>
                                    <div className="text-white text-sm">
                                        <div className="flex">
                                            <div className="basis-6/12 p-3 rounded-l font-bold bg-slate-500 hover:bg-slate-400">{item.title}</div>
                                            <div className="basis-2/12 bg-slate-700 text-center p-3 font-bold">{item.stock}</div>
                                            <div className="basis-3/12 bg-slate-600 text-center p-3 font-bold float-right">${item.price}</div>
                                            <div className="basis-1/12 bg-red-600 hover:bg-red-700 rounded-r text-center  p-3 font-bold float-right cursor-pointer" onClick={() => handleRemove(item.id)}>
                                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-x-lg mx-auto align-middle" viewBox="0 0 16 16">
                                                    <path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z" />
                                                </svg>
                                            </div>
                                        </div>
                                    </div>
                                </li>
                            ))
                        
                    )}

Randy_Johnson's avatar

@Sinnbeck Hey, I found a solution. _.isEmpty(itemsCount). JS is little strange.

edit: my guess is it was empty, but itemCount.length didn't work in this case. I will have to look into arrays and objects.

{_.isEmpty(itemsCount) ? (
                        <p className="text-white text-center">The cart is empty!</p>
                    ) : (
                            itemsCount.map((item, index) => (
                                <li key={index}>
                                    <div className="text-white text-sm">
                                        <div className="flex">
                                            <div className="basis-6/12 p-3 rounded-l font-bold bg-slate-500 hover:bg-slate-400">{item.title}</div>
                                            <div className="basis-2/12 bg-slate-700 text-center p-3 font-bold">{item.amount}</div>
                                            <div className="basis-3/12 bg-slate-600 text-center p-3 font-bold float-right">${item.price}</div>
                                            <div className="basis-1/12 bg-red-600 hover:bg-red-700 rounded-r text-center  p-3 font-bold float-right cursor-pointer" onClick={() => handleRemove(item.id)}>
                                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-x-lg mx-auto align-middle" viewBox="0 0 16 16">
                                                    <path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z" />
                                                </svg>
                                            </div>
                                        </div>
                                    </div>
                                </li>
                            ))

                    )}
Sinnbeck's avatar

@Randy_Johnson only if you are not consistent with what a variable holds :) if you assume a variable is an array, it must always be an array If you change it to null, undefined, a string, an integer or an object, it will fail. Based on the name I would assume it's an integer

Randy_Johnson's avatar

@Sinnbeck Yes, sorry my names are a little miss leading. Originally it was an array.

const items = {id: 1, name: 'apple', price: 2.99}

but something happened here:

        let uniqueItems = arr.reduce((acc, item) => {
            if (!(item.id in acc)) {
                acc[item.id] = item;
            }
            return acc;
        }, {});

But am so new to JS that the facts that its progression was good enough. But here am guessing its turning it into an Object. And I turn it back using const propertyValues = Object.values(uniqueItems)

Sinnbeck's avatar

@Randy_Johnson yeah it can be quite a mouthful. But it's important to try and always keep the original data type

const foo = {id: 1} //object
const foo2 = [1, 2] //array
const foo3 = [{id: 1}, {id: 2}] //array of objects 
1 like

Please or to participate in this conversation.