0

I know this question has been asked before, but it doesn't seem to work for me because what I am trying to do is show the price without an onClick function.

I am setting the price according to planType and billing.

Here is the code:

    this.state = {
      planType: "", //populated through componentDiDMount
      billing: "", //populated through componentDiDMount
      price: 0
    };
  }

  renderPrice() {
    if (
      this.state.billing === "Monthly" &&
      this.state.planType === "Start-Up Plan"
    ) {
      this.setState({
        price: this.state.total_invoice + 35
      });
    }
  }

  render() {
    return <div>You will be charged {this.renderPrice()}</div>;
  }
oneone
  • 32
  • 1
  • 9

2 Answers2

2

Never ever setState inside render, there is a much better way to do what you are trying to do,
I take it you want the price change happens when the billing and the planType are set to a specific values, to do this all you need to do is check and setState the price when and where these two values change!
For example you are saying these two value are getting populated through componentDidMount, so you simply need to check their value in that place and set the price there!, its as simple as that.
Here is some code:

import React, { PureComponent } from 'react';

class MyClass extends PureComponent {
    state = {
        billing: null,
        planType: null,
        total_invoice: 0,
    }

    componentDidMount() {
        //These can change through api or any other way you want!
        const newBillingType = 'Monthly';
        const newPlanType = 'Start-Up Plan';

        this.setState({ billing: newBillingType, planType: newPlanType })

        if (newBillingType === "Monthly" && newPlanType === "Start-Up Plan") {
            this.setState({ price: this.state.total_invoice + 35 })
        }
    }

    render() {

        return <div>You will be charged {this.state.price}</div>;
    }
}

export default MyClass;  

Please note that if you call setState inside a function multiple times (without any timeout) like i did in componentDidMount, because the setState doesn't happen instantly, React will combine the result and only does one setState which is a good thing and avoids multiple updates.

ali ahmadi
  • 179
  • 3
  • 20
  • I had tried it before it didn't work, I tried it again and now it works with two setState's. I will update my code so you can see the solution. Thank you! – oneone Nov 12 '19 at 15:00
  • @oneone, i added a sample of what i just said, hopefully this solves your issue. – ali ahmadi Nov 12 '19 at 15:02
  • Yes, now I got a problem when I change it to another plan, the second setState overrides the number to 35 eveytime. – oneone Nov 12 '19 at 15:07
  • @oneone I saw your solution, and it seems you are forgetting that `setState` is not sync! does not happen instantly so if you call `this.state.total_invoice + 35` two times, it doesn't mean `70`, because the value inside `state` is not yet updated – ali ahmadi Nov 12 '19 at 15:09
  • @oneone There are a lot of answers and question on how to increase something in react, for example https://stackoverflow.com/questions/39316376/how-to-use-the-increment-operator-in-react, please try reading some of them so you can get an idea of how react works, good luck – ali ahmadi Nov 12 '19 at 15:12
  • No, I wasn't duplicating the ```setState``` to achieve ```70``` . I was duplicating it because it just works that way. I updated my code with another planType check and that's when it shows ```35``` instead of ```65``` and I am a beginner in React I have no idea how to solve it. – oneone Nov 12 '19 at 15:13
  • @oneone I suggest you take a look at https://codesandbox.io/ and write your examples and codes there, this enabled you to share your samples with us and we can help you or even correct your code easily. – ali ahmadi Nov 12 '19 at 15:15
0

I actually hesitated to post this answer. This is more like an optimization than the actual answer. But since @oneone asked for it. I will post a general idea on how to improve your code's readability.

One thing to really consider about is, does that value really need to be in state. If that value is only used to compute some other values, maybe you don't have to store it in state. Of course I don't know the actual implementation of the codes so you'll have to figure it out by yourself.

class Component extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      // Do consider if billing / planType is actually necessary to be in state.
      billing: null,
      planType: null,
      price: 0,
      total_invoice: 0,
    };
  } 

  componentDidMount() {
    const { total_invoice } = this.state; 

    const newBilling = 'Monthly';
    const newPlanType = 'Start-Up Plan';

    // default price
    let price = total_invoice;

    this.setState({ billing: newBilling, planType: newPlanType }, () => {
      const { planType } = this.state; 
      // setState callback, state values here are guaranteed to be updated.
      switch (planType) {
        case 'Start-Up Plan':
          price = total_invoice + 35;
          break;
        case 'Standard Plan':
          price = total_invoice + 65;
          break;
        default:
      }
      this.setState({ price });
    });
  }

  render() {
    const { price } = this.state;
    return (
      <div>
        You will be charged {price}
      </div>
    )
  }
}
junwen-k
  • 3,454
  • 1
  • 15
  • 28
  • Thank you so much, this solved my problem 100%! I have never seen anyone use the setState somewhere else as you did. Before that, I was getting an error because of two setStates. But now it works like a charm! – oneone Nov 13 '19 at 09:29
  • Generally in this type of data changes there really is no need to wait for `billing` and `planType` to change and then try to change the price, if you wait, you are practically calling `setState` two times and causing an extra update when you don't really have!, which i believe is a bad practice and should be avoided. – ali ahmadi Nov 13 '19 at 10:39
  • Yes. Generally you should only call `setState` once. I am aware that my codes will trigger rerender for two times. Your answer will call `setState` twice too by the way. That's why I have a note in my codes stating that that if `billing` and `planType` is really needed as a state. If it's only for computing other values, maybe you don't need it to be inside a state. Of course in the end it depends on what the author wants to do. – junwen-k Nov 13 '19 at 10:51
  • I did note in my answer that if you call setState inside a function multiple times (without any timeout) like i did in componentDidMount, because the setState doesn't happen instantly, React will combine the result and only does one setState and avoids multiple updates – ali ahmadi Nov 13 '19 at 10:56