prog
prog

Reputation: 193

React-native: Calculation returns NaN when Input has empty text

I'm building a fee calculator with React-Native, and I have multiple input fields that variables for my calculation are set with.

Everything works well when the input fields have correct values however if the user hits backspace on what they have previously input and then try to calculate profit with an empty field the total shows up as NaN.

Here is my code so far:

this.state = {
       text: '',
       soldPrice: 0,
       shippingCost: 0,
       shippingCharge: 0,
       itemCost: 0,
       profit: 0,
       paypalFee: 0.30,
       paypalFeePercentage: 0.029,
       ebayFee: 0.1
     };
  }

  calculateProfit = () => {
    const { soldPrice, shippingCost, shippingCharge, itemCost,
      paypalFee, ebayFee, paypalFeePercentage
     } = this.state;


     let sp = parseFloat(soldPrice);
     const sc = parseFloat(shippingCost);
     const sCharge = parseFloat(shippingCharge);
     const ic = parseFloat(itemCost);
     const pf = parseFloat(paypalFee);
     const ef = parseFloat(ebayFee);
     const pfp = parseFloat(paypalFeePercentage);

// sp = soldPrice, sc = shippingCost,
// sCharge = shippingCharge, ic = itemCost,
// pf = paypalFee, ef = ebayFee, pfp = paypalFeePercentage, c = calculation


     const calculation = sp - sp * pfp -
     pf
     - sp * ef - sc
     - ic;

     sp += sCharge;

     if (!this.state.itemCost) {
       this.setState({ itemCost: 0 });
     }

     let i;
// if profit is more than 10 than the profit will be displayed as 00.00
// otherwise it will be displayed as 0.00
     if (calculation > 1000) {
       i = 6;
     } else if (calculation > 100) {
       i = 5;
     } else if (calculation > 10) {
       i = 4;
     }
     const roundedNumber = calculation.toPrecision(i);
    this.setState({
      profit: roundedNumber
    });
  }

 render() {
   return (
     <View style={styles.container}>

     <View style={styles.header}>
     <Text style={styles.headerText}>Efees</Text>
     </View>

     <View style={styles.blocks}>

     <View style={styles.inputs}>
     <Text style={styles.inputText}>Sold Price</Text>

     <TextInput
         underlineColorAndroid='transparent'
         style={styles.TextInputStyle}
         placeholder='0.00'
         keyboardType={'numeric'}
         value={this.state.soldPrice}
         onChangeText={(soldPrice) => this.setState({ soldPrice })}
     />

     </View>

     <View style={styles.inputs}>
     <Text style={styles.inputText}>Shipping Charge</Text>

     <TextInput
         underlineColorAndroid='transparent'
         style={styles.TextInputStyle}
         placeholder='0.00'
         keyboardType={'numeric'}
         value={this.state.shippingCharge}
         onChangeText={(shippingCharge) => this.setState({ shippingCharge })}
     />

     </View>

     <View style={styles.inputs}>
     <Text style={styles.inputText}>Shipping Cost</Text>

     <TextInput
         underlineColorAndroid='transparent'
         style={styles.TextInputStyle}
         placeholder='0.00'
         keyboardType={'numeric'}
         value={this.state.shippingCost}
         onChangeText={(shippingCost) => this.setState({ shippingCost })}
     />

     </View>

     <View style={styles.inputs}>
     <Text style={styles.inputText}>Item Cost</Text>

     <TextInput
         underlineColorAndroid='transparent'
         style={styles.TextInputStyle}
         placeholder='0.00'
         keyboardType={'numeric'}
         value={this.state.itemCost}
         onChangeText={(itemCost) => this.setState({ itemCost })}
     />

     </View>

     <View style={styles.inputs}>



            </View>

     </View>

     <TouchableOpacity
     style={styles.calcButton}
     onPress={this.calculateProfit}
     >
        <Text style={styles.calcText}>Calculate </Text>
      </TouchableOpacity>

      <Text>{`Profit: $${this.state.profit}`}</Text>


      </View>
 );
 }
}

Upvotes: 2

Views: 3393

Answers (1)

Dacre Denny
Dacre Denny

Reputation: 30390

The issue here is caused by parseFloat() returning Number.NaN when attempting to parse an empty string:

/* Parse a valid value */
console.log( parseFloat('1.2') , '===', 1.2 );

/* Parse an empty string yields NaN */
console.log( parseFloat('') , '===', Number.NaN );

When a Number.NaN is introduced into the arithmetic of your subsequent calculation by one or more variables, the value of calculation will be Number.NaN:

const sp = 1,
      pfp = 1,
      ef = 1,
      pf = 1,
      sc = 1,
      ic = Number.NaN;

const calculation = sp - sp * pfp - pf - sp * ef - sc - ic;

console.log(calculation);

There are many ways to resolve this issue - a simple method that requires minimal change with your current code would be as follows:

calculateProfit = () => {
  const {
    soldPrice,
    shippingCost,
    shippingCharge,
    itemCost,
    paypalFee,
    ebayFee,
    paypalFeePercentage
  } = this.state;

  /* 
  Define helper function that recovers a 0 as a default value
  if "non-a-number" results from parseFloat
  */
  const safeParseFloat = (str) => {

    const value = Number.parseFloat(str);

    return Number.isNaN(value) ? 0 : value;    
  }

  /*
  Use locally defined helper to safely parse strings while also
  accounting form empty string mapping to 0
  */
  let sp = safeParseFloat(soldPrice);
  const sc = safeParseFloat(shippingCost);
  const sCharge = safeParseFloat(shippingCharge);
  const ic = safeParseFloat(itemCost);
  const pf = safeParseFloat(paypalFee);
  const ef = safeParseFloat(ebayFee);
  const pfp = safeParseFloat(paypalFeePercentage);

  const calculation = sp - sp * pfp - pf - sp * ef - sc - ic;

  sp += sCharge;

  if (!this.state.itemCost) {
    this.setState({
      itemCost: 0
    });
  }

  let i;
  // if profit is more than 10 than the profit will be displayed as 00.00
  // otherwise it will be displayed as 0.00
  if (calculation > 1000) {
    i = 6;
  } else if (calculation > 100) {
    i = 5;
  } else if (calculation > 10) {
    i = 4;
  }
  const roundedNumber = calculation.toPrecision(i);

  this.setState({
    profit: roundedNumber
  });
}

Upvotes: 6

Related Questions