Reputation: 111
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
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
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
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