Javascript Calculator : React, Gatsby, IOS styling

This is the second version of my Calculator. I couldn’t get my first to pass user story 9 so I started over and used the dreaded eval and strings for storing my input values as that seemed easier to manipulate in React (could be wrong about that).

Anyway, any tips, advice, thoughts will be appreciated.

Calculator is here

Code

1 Like

It would be nice to see the equation: 2x4 and the the sum = 8. Also the percent isn’t working.

Thanks Helen!

The toggle negative and percent buttons are extras but I do intend to get them working. I also agree about how I am displaying the history. I know I did it that way because it helped me solve one of the user stories. But I kind of have a few ideas to work around that.

I haven’t looked at the code. I’m on my phone and code pen isn’t very responsive.

Sorry didn’t include the code earlier. I linked it in the OP now. :slight_smile:

The design is nice, although I recommend making the background a lighter color to contrast with the calculator.

If I keep pressing numbers in the calculator they get smushed behind the buttons. Also I really recommend trying to code the logic without eval. Using eval will negate all the optimizations the JS engine makes based on the lexing phase (i.e. your code will run slower). It’s not enough for a user to notice but a prospective future employer might care.

I suggest looking up “finite state machine” as a way to organize your logic. Once you map out all the possibilities you could go from a certain state (like after pressing the decimal button) it’s fairly straightforward coding.

I decided to have a go at this and I am right back at the problem that led me to using eval(). I think my problem is partly that when I just add in (no pun intended) some simple math logic it solves most of the user stories for me and then my solutions tend to break stuff that was working before.

My initial thought is that when I hit an operator button a second time, 1) I should perform the math for the first part of the operation and 2) use that result as the first entry in my ‘values’ array?

Any thoughts are welcome.

import React, { Component } from 'react'
import Layout from '../components/layout'
import SEO from '../components/seo'
import styled from 'styled-components'


const CalcWrapper = styled.div`
  margin: 0 auto;
  padding: 0;
  margin-top: 100px;
  display: grid;
  width: 326px;
  grid-template-columns: auto;
  justify-content: center;
  box-shadow: 0px 5px 44px 0px rgba(0,0,0,0.8);
  border-radius: 10px;
  background: #222;
`
  
const Keypad = styled.div`
  display: grid;
  grid-template-columns: repeat(4, 80px);
  grid-gap: 2px;
  justify-content: center;
  
  button {
    font-size: 1.5rem;
    height: 60px;
  }
`
const LastRow = styled.div`
  display: grid;
  grid-template-columns: 162px 80px 80px;
  grid-gap: 2px;
  justify-content: center;
  margin: auto;
`
const CalcDisplay = styled.div`
  display: grid;
  grid-template-columns: 320px;
  color: #fff;
  height: 80px;
  font-size: 2.3rem;
  padding: 8% 0 4%;
  justify-content: center;
  text-align: right;
  padding-right: 40px;
  // background: #222;
`
const OpBtn = styled.button`
  background-color: orange;
  color: #fff;
  border: none;
`

const SpecBtn = styled.button`
  background-color: #444;
  border: none;
  color: #fff;
`
const NumBtn = styled.button`
  background-color: #888;
  border: none;
  color: #fff;
`

const MathView = styled.div`
  color: #777;
  padding-top: 8px;
  height: 35px;
  // background: #222;
`

export default class IndexPage extends Component {
  constructor(props) {
    super(props)

    this.state = {
      amount: 0,
      values: [],
      operator: '',
      history: '',
      dec: false,
    }
  }

  handleClear = (e) => {
    console.log('Clear!!!');
    this.setState({
      amount: 0,
      values: [],
      operator: '',
      history: '',
      dec: false
    })
  }

  handleDecimal = (e) => {
    if (!this.state.dec) {
      console.log(`Decimal!`);
      this.setState({
        history: this.state.history + e.target.value, // eslint-disable-next-line
        amount: this.state.amount += e.target.value,
        dec: true
      })
    }
    
  }

  handleKeyPad = (e) => {
    console.log(`Numbaaahs! ${e.target.value}`);
    this.setState({
      history: this.state.history + e.target.value, // eslint-disable-next-line
      amount: this.state.amount += e.target.value,
    }, () => {
      this.setState({
        amount: this.state.amount.replace(/0?/,'')
      })
    })

  }

  handleOpKey = (e) => {
   console.log(`Smooth Operator ${e.target.value}`);
   this.setState({
     operator: e.target.value,
     dec: false,
     amount: 0,
     history: this.state.history + e.target.value,
     values: [...this.state.values, ...this.state.amount]
   })
    
  }
  
  handleCalc = (e, previousState) => {
    console.log(`Calculating!!!`);
    
    this.setState({
      values: [...this.state.values, ...this.state.amount],
    }, () => {
      console.log(this.state.values);
      console.log(this.state.values[0]);
      console.log(this.state.values[1]);
      let num1 = parseFloat(this.state.values[0])
      let num2 = parseFloat(this.state.values[1])
      let result = 0

      if ( this.state.operator  === '+'  ) {
        result = num1 + num2
      } 
      if (this.state.operator === '-') {
        result = num1 - num2
      }
      if (this.state.operator === '*') {
        result = num1 * num2
      }
      if (this.state.operator === '/') {
        result = num1 / num2
      }
      
      this.setState({
        dec: false,
        amount: result,
        values: [],
      })
    })
    this.setState({
      values: [this.state.values, ...this.state.amount]
    })
  }
  

  render() {
    return (
      <Layout>
        <SEO title="Home" keywords={[`freecodecamp`, `application`, `calculator`]} />
          <CalcWrapper>

            <MathView>{this.state.history}</MathView> 
            <CalcDisplay id="display">
            {this.state.amount}
            </CalcDisplay>

            <Keypad>

              <SpecBtn id="clear" onClick={this.handleClear} data-action="clear">AC</SpecBtn>
              <SpecBtn id="negative" onClick={this.toggleNegative} data-action="negativToggle">+/-</SpecBtn>
              <SpecBtn id="percentage" onClick={this.toggleNegative} data-action="percentage">%</SpecBtn>
              <OpBtn id="add" value="+" onClick={this.handleOpKey} data-action="add">+</OpBtn>

              <NumBtn id="one" value="1" onClick={this.handleKeyPad}>1</NumBtn>
              <NumBtn id="two" value="2" onClick={this.handleKeyPad}>2</NumBtn>
              <NumBtn id="three" value="3" onClick={this.handleKeyPad}>3</NumBtn>
              <OpBtn id="subtract" value="-" onClick={this.handleOpKey} data-action="subtract">-</OpBtn>

              <NumBtn id="four" value="4" onClick={this.handleKeyPad}>4</NumBtn>
              <NumBtn id="five" value="5" onClick={this.handleKeyPad}>5</NumBtn>
              <NumBtn id="six" value="6" onClick={this.handleKeyPad}>6</NumBtn>
              <OpBtn id="multiply" value="*" onClick={this.handleOpKey} data-action="multiply">x</OpBtn>


              <NumBtn id="seven" value="7" onClick={this.handleKeyPad}>7</NumBtn>
              <NumBtn id="eight" value="8" onClick={this.handleKeyPad}>8</NumBtn>
              <NumBtn id="nine"  value="9" onClick={this.handleKeyPad}>9</NumBtn>
              <OpBtn id="divide" value="/" onClick={this.handleOpKey} data-action="divide">÷</OpBtn>

              <LastRow>
                <NumBtn id="zero" value="0" onClick={this.handleKeyPad}>0</NumBtn>
                <NumBtn id="decimal" value="." onClick={this.handleDecimal} data-action="decimal">.</NumBtn>
                <OpBtn id="equals" value="=" type="button" onClick={this.handleCalc}>=</OpBtn>
              </LastRow>

            </Keypad>
          </CalcWrapper>
      </Layout>
    )
  }
}