0

So I have code here that is supposed to calculate a price based off of property sqft and the package selected.

The code used to work with class components when I had it written like so... but Im now using react JS and Functional components.

The current issue I am having right now is getting the setBasePrice(1234); to actually set the base price so it can be calculated in the function.

Im also having an issue with getting the basePrice += basePrice * (percentage / 100); to work properly in the functional component.

I know I have this totally wrong right now just hoping someone can point me in the right direction and possibly show me how to clean up my code.

UPDATED

I added the useEffect hook and the total price is coming up incorrectly. at 5500 sqft the price should be $272 but comes up as $112. My old code doesnt do this so im not sure whats going on.

Class Component

 this.state = {
      propertySqft: "",
      packageSelected: this.props.location.state,
      totalPrice: null,
      basePrice: null
    };
  }

  checkPrice = () => { 
      debugger;
       if(this.state.packageSelected === "Photography"){
        this.state.basePrice = 159.998;
       }else if(this.state.packageSelected === "Video"){
            this.state.basePrice = 416.998;
       }else if(this.state.packageSelected === "Drone Aerial Photos Only"){
            this.state.basePrice = 199.998;
       }else if(this.state.packageSelected === "Drone Aerial Video Only"){
            this.state.basePrice = 249.998;
       }else if(this.state.packageSelected === "Drone Aerials Photo & Video"){
            this.state.basePrice = 299.998;
       }else if(this.state.packageSelected === "Photography, Video, Drone Aerials"){
            this.state.basePrice = 559.998;
       }if(this.state.propertySqft > 2500){
   let overage = this.state.propertySqft - 2000;
   let percentage = Math.floor(overage / 500) * 10;
   this.state.basePrice += this.state.basePrice * (percentage / 100);


  }
     this.setState({ totalPrice: Math.round(this.state.basePrice * 100) / 100 }); 
  }

Functional Component

const [basePrice, setBasePrice] = useState(0);
const [newPrice, setNewPrice] = useState(null);
const [totalPrice, setTotalPrice] = useState(null);

useEffect(() => {
  setTotalPrice(basePrice => (Math.round(basePrice * 100) / 100));
}, [basePrice]); 

const checkPrice = () => { 
      debugger;
       if(packageSelected.packages === "Photography"){
         setBasePrice(159.998);
       }else if(packageSelected.packages === "Video"){
         setBasePrice(516.998);
       }else if(packageSelected.packages === "Drone Aerial Photos Only"){
         setBasePrice(119.998);
       }else if(packageSelected.packages === "Drone Aerial Video Only"){
         setBasePrice(269.998);
       }else if(packageSelected.packages === "Drone Aerials Photo & Video"){
         setBasePrice(549.998);
       }else if(packageSelected.packages === "Photography, Video, Drone Aerials"){
         setBasePrice(566.998);   
       }if(propertySqft > 2000){
   const overage = propertySqft - 2000;
   const percentage = Math.floor(overage / 500) * 10;
    setBasePrice(basePrice => (basePrice * (percentage / 100)));
 }
  };
Jordan Spackman
  • 333
  • 1
  • 6
  • 23
  • 1
    what errors are showing? can you add that so we can see? – mph85 Nov 27 '19 at 19:27
  • Why dont you initialize you prices with `0` instead of `null`, that way the type will always be `number` and it can never fail. – oemera Nov 27 '19 at 19:30
  • Im actually not getting any errors, its just that when i call setBasePrice(339.998); it doesnt set the basePrice – Jordan Spackman Nov 27 '19 at 19:30
  • There's really no need to have a state in this component. I don't understand why you'd want to change a working component only to use hooks? – Emile Bergeron Nov 27 '19 at 19:32
  • Also, the reason it doesn't work is because you were misusing the state in the class component to begin with. `this.state.basePrice = 249.998;` mutates the state and is an anti-pattern. – Emile Bergeron Nov 27 '19 at 19:33
  • Possible duplicate of [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Emile Bergeron Nov 27 '19 at 19:33
  • @oemera When I initialize with 0 it stays 0 the whole time for some reason. the setBasePrice is completely ignored it seems like. – Jordan Spackman Nov 27 '19 at 19:34
  • [Why can't I directly modify a component's state, really?](https://stackoverflow.com/q/37755997/1218980) – Emile Bergeron Nov 27 '19 at 19:35
  • @Emile Bergeron It's always better to migrate to the recommended way and keep your code consistent. – oemera Nov 27 '19 at 19:37
  • @oemera Class components aren't deprecated, there's no reason to migrate. Hooks are solving a really specific issue with shared behaviour composition in huge applications, otherwise completely unnecessary. – Emile Bergeron Nov 27 '19 at 19:38
  • 2
    I think he's now learning React hooks and just wants to know how a class component would directly translate to a functional component – mph85 Nov 27 '19 at 19:40
  • 1
    @mph85 I agree that learning is the only other situation where it would make sense to translate a class component to a function component with hooks. – Emile Bergeron Nov 27 '19 at 19:42
  • I never said it's deprecated. Dan Abramov recommended to use functional components with hooks and use hooks in existing project, when you create new components. Considering that, my main point was to keep the code consistent.You don't have to migrate, I just think it's better if you have the time. – oemera Nov 27 '19 at 19:44
  • @oemera _"We recommend avoiding any “big rewrites”, especially for existing, complex class components."_ directly taken from the [documentation on hooks](https://reactjs.org/docs/hooks-intro.html#gradual-adoption-strategy). – Emile Bergeron Nov 27 '19 at 19:46
  • The same paragraph implies that it would be good to migrate, but that you shouldn't do right away => "There is no rush to migrate to hooks." Otherwise migrating wouldn't even be mentioned. "we will keep supporting class components for the foreseeable future" => Main focus will be hooks. – oemera Nov 27 '19 at 19:53
  • @oemera you're incorrectly implying things. They mention that there's no rush to migrate since it's not meant as a replacement to class components and then they ensure that class components will be kept and they're not planning to deprecate them. They wrote this explicitly to avoid the confusion that everyone should use hooks and that class components shouldn't be used. Hooks are definitely clean and improve reusability. – Emile Bergeron Nov 27 '19 at 19:59
  • 1
    I think we both at least agree that this is not the right place to discuss this any further :) I see your point, tho – oemera Nov 27 '19 at 20:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/203203/discussion-between-oemera-and-emile-bergeron). – oemera Nov 27 '19 at 20:43

1 Answers1

5
  1. When you want to set the base price depending on the value of base price earlier, you can use the callback version of setBasePrice function:
setBasePrice(basePrice => (basePrice + (basePrice * (percentage / 100)));

This way you know that basePrice value would be calculated from the latest.

Docs

  1. From what I understand, you need to calculate total amount whenever the base price changes. Instead of calculating this everywhere basePrice changes, you can add this to a useEffect
useEffect(() => {
  setTotalPrice(Math.round(basePrice * 100) / 100);
}, [basePrice]);

In your current code, because setBasePrice is async, it will not have completed execution when setTotalPrice runs and hence will be a step behind. Here, basePrice is added as a dependency to useEffect and it will be updated once basePrice changes.

PS: Your class component is also setting a value directly to state which is wrong, it should be using a setState to perform that update.

Agney
  • 18,522
  • 7
  • 57
  • 75
  • 1
    @Agney, great answer! Just a quick question. Do you think to use a callback to setState within useEffect is the simplest way to handle component wide async states? – Chris Chen Nov 27 '19 at 19:45
  • yes, that would be the declarative way to go about it – Agney Nov 27 '19 at 19:50
  • So for some reason the base price still comes up as 0 or undefined. Im not sure what im doing wrong. – Jordan Spackman Nov 27 '19 at 19:55
  • Are you sure `checkPrice` is getting called? If so, you might update your post with the corresponding code including the suggested changes from @Agney – oemera Nov 27 '19 at 20:10
  • Yes it is getting called and I just updated thanks for reminding me! The price is coming up as the wrong price now just need to figure out why. – Jordan Spackman Nov 27 '19 at 20:41
  • I wonder if the += has anything to do with why it isnt working properly. – Jordan Spackman Nov 27 '19 at 20:42
  • You are right @Agney 's code doesn't replicate the functionality 100%. It has to be `setBasePrice(basePrice => {return basePrice + (basePrice * (percentage / 100)})` – oemera Nov 27 '19 at 21:11
  • @oemera Good catch, hadn't noted the `+=` – Agney Nov 28 '19 at 05:03