Update key in state when another key is updated

Asked

Viewed 357 times

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.

Inside the key products own a object with various products each containing the value, and the total of purchase products: ordered and price, example:

state = {
    products: {
        "a": {
            name: "A",
            price: 2
            ordered: 5
        },
        "b": {
            name: "B",
            price: 5,
            ordered: 2
        }
    },
    orderTotal: 0
}

I tried to put a reduce() inside componentWillUpdate(), but I can’t use this.setState() inside componentWillUpdate() because it 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 products is updated, perform a loop by calculating ordered * price and store the sum of everything in orderTotal, what method I can use?

  • The Cart component receives the product object via props?

  • @Not Miguelangelo, the state orderTotal is only in the parent component is not passed to any other component.

  • But what about the object products? Where he comes from?

  • It is started empty through the builder, for the user to add products I pass a function to a child component that manages to add other objects within products

2 answers

3


About setState

How data is stored in state of Carrinho, then the best way to activate a new rendering 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 being said, there are two alternatives to changing 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 to use setState?

  • A: Inside the callback that 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)
        }
    });
  }
}
  • Miguel I think my question was not clear enough, the child components of the "cart" do not change the state, to facilitate understanding please see this file link on github, when I pass the products to the child components they only render and to "change" this prop I pass a function via prop to be able to update the state of the parent component.

  • In this case, it is worth only the second point of my answer. See that within addProduct I call recalculaTotal.

  • understood, is that the value needs to be recalculated several times, both when a product is added and change of quantity and etc.

  • Exactly... I talked about it. I erased the first point that made no sense.

  • the React by itself just has no method of type then? to perform "watch" in the state change... ?

  • I think React’s proposal is this... the paths that lead to changing a data is linear, predictable. Unlike the Angular for example, which allows these cascades of events.

  • when you pass the function addProd for the component of produto in theory we would have to use a onChange side of the product component and call the function addProd with this.props.adicionar()? I didn’t quite get that part...

  • That’s it... in my example the product component would call this.props.add by passing as arguments the product data, or this.props.remove pro otherwise... remembering that when you pass a callback to a component vc have to give bind to this, or use the lambda notation that already binds automatically.

  • @Rafaelacioly Did you solve it? Was it clear what I said? If not I try otherwise. =)

Show 4 more comments

1

In the documentation official says this.setState() should not be called within the componentWillUpdate, if you want to update the state when a property is modified you should do this within the method componentWillReceiveProps.

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 cannot call this.setState() here. If you need to update state in Sponse to a prop change, use componentWillReceiveProps() Instead.

  • The updated state is in a "parent component", I pass an object to a child component along with a function, so what is being changed is the state not a prop.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.