Question :
The main idea is based on a shopping cart, when a product is added to the cart (key products
) the key orderTotal
is updated.
Within the products
key I have a object
with several products that each contain the value, and total purchase products: ordered
and price
, for example:
state = {
products: {
"a": {
name: "A",
price: 2
ordered: 5
},
"b": {
name: "B",
price: 5,
ordered: 2
}
},
orderTotal: 0
}
I’ve tried to put reduce()
inside componentWillUpdate()
, but I can not use this.setState()
within componentWillUpdate()
since this generates an infinite loop.
componentWillUpdate () {
const products = {...this.state.products};
const orderTotal = Object.keys(products).reduce((sum, next) => {
return sum + (products[next].ordered * parseFloat(products[next].price));
}, 0);
this.setState({ orderTotal }); // Loop infinito.
}
I need every time that products
is updated, looping by compiling ordered * price
and storing the sum of everything in orderTotal
, what method can I use?
Answer :
About setState
Because the data is stored in state
of Carrinho
, then the best way to enable a new render is by using setState
of Carrinho
. This new rendering will be done as soon as possible, but not immediately … it’s like a setTimeout passing 0.
Important This means that the state is not changed immediately after the setState call.
That said, there are two alternatives to change the state, if more than one change depends on the previous state:
-
change everything at once:
setState({total: valorTotal, partes: valoresDasPartes })
-
change using callback:
setState({partes: valoresDasPartes}) setState((s,p) => {total: s.partes.reduce((a, b) => a + b, 0)})
The question that remains is where should you use setState
?
- A: Within the callback Cart passes to the child.
Example
class Carrinho extends React.Component {
render() {
var listaProds = []
for (var n = 1; n < 10; n++)
listaProds.append(
<Produto
nome={"Produto "+n}
adicionar={p => this.addProd(p, +1)}
remover={ p => this.addProd(p, -1)}
/>)
return (
<div>
<div>{listaProds}</div>
<div>Total: {this.state.orderTotal}</div>
</div>
)
}
addProd(p, quant) {
// adicionando o produto
this.setState(function (state) {
var s = {}
s.products = state.products
p = s.products[p.key] = s.products[p.key] || Object.assign({}, p, {ordered: 0})
p.ordered = Math.max(p.ordered + quant, 0)
return s
});
this.recalculaTotal()
}
recalculaTotal() {
this.setState(function (state) {
return {
orderTotal: state.products
.map((p) => p.ordered * parseFloat(p.price))
.reduce((a, b) => a + b, 0)
}
});
}
}
In the official documentation this.setState () should not be called within componentWillUpdate
, if you want to update the state when a property is modified, you must do this within the componentWillReceiveProps
method.
componentWillUpdate () is invoked immediately before rendering when new
props or state are being received. Use this as an opportunity to
perform preparation before an update occurs. This method is not called
for the initial render.
Note that you can not call this.setState () here. If you need to update
state in response to a prop change, use componentWillReceiveProps ()
instead.