sienki_jenki
sienki_jenki

Reputation: 111

componentDidMount updating synchronously

I'm loading data from saved session using:

componentDidMount() {
    if (JSON.parse(localStorage.getItem('savedData')) !== null) {
        this.setState({
            cartItems: JSON.parse(localStorage.getItem('savedData')),
            totalPrice: this.getPriceOnLoad(),
            totalItems: this.getItemsOnLoad(),
        });
    }
}

cartItems is an array of objects. Which seems is updated before

this.getPriceOnLoad();
this.getItemsOnLoad();

functions are called, for example this.getPriceOnLoad function:

getPriceOnLoad() {
    let itemsPrice = 0;
    for (let i = 0; i <= this.state.cartItems.length - 1; i++) {
        itemsPrice += this.state.cartItems[i].quantity * this.state.cartItems[i].price;
    }
    return itemsPrice;
}

but, in getPriceOnLoad function, this.state.cartItems.length is equal to 0, so for loop is not executing. I can see in React dev tools that this array has some length. Is it because componentDidMount() is executing state change synchronously and can't see updated array immediately? So my question is how could i update price and quantity of items after array is initialized?

Upvotes: 2

Views: 79

Answers (3)

Treycos
Treycos

Reputation: 7492

Your state is not updated in order. For this, you could store the cartItems in a temporary value, and send it to each functions :

componentDidMount() {
    if (JSON.parse(localStorage.getItem('savedData')) !== null) {
        const cartItems = JSON.parse(localStorage.getItem('savedData'))
        this.setState({
            cartItems, //Short syntax for 'cartItems: cartItems'
            totalPrice: this.getPriceOnLoad(cartItems),
            totalItems: this.getItemsOnLoad(cartItems),
        });
    }
}

You could also make your function significantly shorter by using reduce:

this.setState({
    cartItems,
    totalPrice: cartItems.reduce((total, item) => total + (item.quantity * item.price), 0),
    totalItems: cartItems.reduce((total, item) => total + item.quantity, 0),
});

Can you show us your second function too ? It may be optimized as well. Done.

Upvotes: 4

Prince Hernandez
Prince Hernandez

Reputation: 3721

the wrong thing that you are doing is trying to use values from the state on your functions that will define your state.

you have 2 approaches to solve this:

1) use the callback function from setState and then set the state again with the new data (which into my opinion is not the best approach)

componentDidMount() {
    if (JSON.parse(localStorage.getItem('savedData')) !== null) {
        const cartItems = JSON.parse(localStorage.getItem('savedData'))
        this.setState({
            cartItems
        }, ()=> {
           this.setState({
            totalPrice: this.getPriceOnLoad(cartItems),
            totalItems: this.getItemsOnLoad(cartItems),
           });
        })
    }
}

2) send the values to your functions

componentDidMount() {
    if (JSON.parse(localStorage.getItem('savedData')) !== null) {
        const savedCartItems = JSON.parse(localStorage.getItem('savedData'))
        this.setState({
            cartItems,
            totalPrice: this.getPriceOnLoad(savedCartItems),
            totalItems: this.getItemsOnLoad(savedCartItems),
        });
    }
}

Upvotes: 2

U Rogel
U Rogel

Reputation: 1941

getPriceOnLoad() is executed before this.setState is executed. So you cannot refer to this.state in getPriceOnLoad().

When you call this.setState({}), JS first needs to generate the object for the setState() function. Means the functions you are referring to run first, then this.setState().

And in any case this.setState() is an asynchronous function, so this.state is not directly available after setState() execution.

Upvotes: 0

Related Questions