Update key in state when another key is updated

3

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?

    
asked by anonymous 26.07.2017 / 17:35

2 answers

3

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)
        }
    });
  }
}
    
02.08.2017 / 18:48
1

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.

    
30.07.2017 / 00:16