Etoya
Etoya

Reputation: 239

React Calculator

I am trying to build React calculator. So far it works only if the length of my second number is equal to 1. if it's more digits it's gonna break. Here is the code:

class Calculator extends Component {
  constructor(props) {
      super(props);
      this.state = {
        current: 0,
        total: 0,
        operator: ''
      };
  }
  handleType = (e) =>{
    const current = (this.state.current == 0 || this.state.operator != '' || this.state.current == this.state.total ) ? '' : this.state.current
    this.setState({
      current: parseInt(current + e.target.attributes.getNamedItem('data-filter').value)
    });
  }
calculate = (sign, number) => {
  const total = this.state.total;
  console.log(total);
  switch(sign){
     case "-":
      return total - number;
        break;
     case "+":
        return  total + number;
        break;
     case "*":
        return  total * number;
        break;
     case "/":
       return  total / number;
        break;
     default:
       return 0;
  }
};

handleAction = (e) =>{
  const operator = e.target.attributes.getNamedItem('data-filter').value;
  console.log(operator);
  this.setState({
     total: this.state.current,
     operator: operator
  });
  console.log(this.state);
}
getResult = () =>{
  this.setState({
     current: this.calculate(this.state.operator, this.state.current)
    });
}

  render() {
    return (
      <div className="Calculator text-center">
          <h2>Result: {this.state.current} </h2>
          <div className="row">
              <button data-filter="7" onClick={this.handleType}>7</button>
              <button data-filter="8" onClick={this.handleType} >8</button>
              <button data-filter="9" onClick={this.handleType} >9</button>
              <button data-filter="+" onClick ={this.handleAction} >+</button>
          </div>
          <div className="row">
              <button data-filter="4" onClick={this.handleType}>4</button>
              <button data-filter="5" onClick={this.handleType}>5</button>
              <button data-filter="6" onClick={this.handleType}>6</button>
              <button data-filter="-" onClick ={this.handleAction}  >-</button>
          </div>
          <div className="row">
              <button data-filter="1" onClick={this.handleType}>1</button>
              <button data-filter="2" onClick={this.handleType}>2</button>
              <button data-filter="3" onClick={this.handleType}>3</button>
              <button data-filter="*" onClick ={this.handleAction}  >*</button>
              <button data-filter="/" onClick ={this.handleAction}  >/</button>
          </div>
          <div className="row">
              <button>0</button>
              <button>save</button>
              <button>cancel</button>
              <button onClick={this.getResult}>=</button>
          </div>

      </div>
    );
  }
}

I understand that the problem is my condition:

  const current = (this.state.current == 0 || this.state.operator != '' || this.state.current == this.state.total ) ? '' : this.state.current

It works fine for the first number, when operator='' but once I have an operator it will not allow me to make my number greater than 9.

Here is the codepen demo http://codepen.io/polinaz/pen/PmLoKJ?editors=0010

Any ideas how to fix it? Thank you:)

Upvotes: 1

Views: 1709

Answers (2)

user2340824
user2340824

Reputation: 2152

I've made a number of changes in your code:

As State Updates can be Asynchronous (https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous), it's very bad practise to use previous state values in the way you are using:

this.setState({
    current: parseInt(current + e.target.attributes.getNamedItem('data-filter').value)
});

I have change a number of these into this format:

this.setState((prevState) => {
  const current = prevState.current;
  return {current: parseInt(current + value)};
});

I moved the e.target.attributes.getNamedItem('data-filter').value code into it's own variable due to event-pooling

handleType (e) {
    const value = e.target.attributes.getNamedItem('data-filter').value;

The major bug you were facing was, after the Operator was selected (via handleAction), your operator attribute was populated. This mean in your handleType, you did the check: this.state.operator != ''

const current = (this.state.current == 0 || this.state.operator != '' || this.state.current == this.state.total ) ? '' : this.state.current

This meant you were disregarding the previously selected current value. setting it to '' and then appending it to the the current value.

class Calculator extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
        current: 0,
        total: 0,
        operator: ''
      };
    this.handleType = this.handleType.bind(this);
    this.calculate = this.calculate.bind(this);
    this.handleAction = this.handleAction.bind(this);
     this. getResult = this.getResult.bind(this);
    
  }
  
  handleType (e) {
    const value = e.target.attributes.getNamedItem('data-filter').value;

    this.setState((prevState) => {
      const current = (this.state.current == 0 || this.state.current == this.state.total ) ? '' : this.state.current;
      return {current: parseInt(current + value)};
    });
      
  }
calculate (sign, number) {
  const total = this.state.total;
  console.log(total);
  switch(sign){
     case "-":
      return total - number;
        break;
     case "+":
        return  total + number;
        break;
     case "*":
        return  total * number;
        break;
     case "/":
       return  total / number;
        break;
     default:
       return 0;
  }
};

handleAction (e) {
  const operator = e.target.attributes.getNamedItem('data-filter').value;
  
  this.setState((prevState) => {
   return {
     total: prevState.current,
     operator: operator,
     current: 0
   } 
  });
  
  console.log(this.state);
}
getResult () {
  this.setState({
     current: this.calculate(this.state.operator, this.state.current)
    });
}

  render() {
    return (
      <div className="Calculator text-center">
          <h2>Result: {this.state.current} </h2>
          <div className="row">
              <button data-filter="7" onClick={this.handleType}>7</button>
              <button data-filter="8" onClick={this.handleType} >8</button>
              <button data-filter="9" onClick={this.handleType} >9</button>
              <button data-filter="+" onClick ={this.handleAction} >+</button>
          </div>
          <div className="row">
              <button data-filter="4" onClick={this.handleType}>4</button>
              <button data-filter="5" onClick={this.handleType}>5</button>
              <button data-filter="6" onClick={this.handleType}>6</button>
              <button data-filter="-" onClick ={this.handleAction}  >-</button>
          </div>
          <div className="row">
              <button data-filter="1" onClick={this.handleType}>1</button>
              <button data-filter="2" onClick={this.handleType}>2</button>
              <button data-filter="3" onClick={this.handleType}>3</button>
              <button data-filter="*" onClick ={this.handleAction}  >*</button>
              <button data-filter="/" onClick ={this.handleAction}  >/</button>
          </div>
          <div className="row">
               <button data-filter="0" onClick={this.handleType}>0</button>
              <button>save</button>
              <button>cancel</button>
              <button onClick={this.getResult}>=</button>
          </div>

      </div>
    );
  }
}

ReactDOM.render(
  <Calculator/>,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='root'></div>

Upvotes: 1

Mayank Shukla
Mayank Shukla

Reputation: 104379

Did some changes, refer the snippet.

Working snippet:

class Calculator extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
        current: '',
        total: 0,
        operator: ''
      };
  }

  handleType = (e) =>{
    const current = (this.state.current == 0 || this.state.operator != '' || this.state.current == this.state.total ) ? '' : this.state.current
    this.setState({
      current: this.state.current + e.target.attributes.getNamedItem('data-filter').value
    });
  }

  calculate() {
    const total = parseInt(this.state.total);
    const current = parseInt(this.state.current);
    const sign = this.state.operator;
    switch(sign){
       case "-":
        return total - current;
          break;
       case "+":
          return  total + current;
          break;
       case "*":
          return  total * current;
          break;
       case "/":
          return  total / current;
          break;
       default:
          return 0;
     }
  };

  handleAction = (e) =>{
    const operator = e.target.attributes.getNamedItem('data-filter').value;
    this.setState({
       total: this.state.current,
       current: '',
       operator: operator
    });
  }

  getResult = () => {
    this.setState({
       total: this.calculate(),
       current: ''
    });
  }
  
  cancel = () => {
     this.setState({
        total: '',
        current: '',
        operator: ''
     })
  }

  render() {
    return (
      <div className="Calculator text-center">
          Number: {this.state.current}
          <h2>Result: {this.state.total} </h2>
          <div className="row">
              <button data-filter="7" onClick={this.handleType}>7</button>
              <button data-filter="8" onClick={this.handleType} >8</button>
              <button data-filter="9" onClick={this.handleType} >9</button>
              <button data-filter="+" onClick ={this.handleAction} >+</button>
          </div>
          <div className="row">
              <button data-filter="4" onClick={this.handleType}>4</button>
              <button data-filter="5" onClick={this.handleType}>5</button>
              <button data-filter="6" onClick={this.handleType}>6</button>
              <button data-filter="-" onClick ={this.handleAction}  >-</button>
          </div>
          <div className="row">
              <button data-filter="1" onClick={this.handleType}>1</button>
              <button data-filter="2" onClick={this.handleType}>2</button>
              <button data-filter="3" onClick={this.handleType}>3</button>
              <button data-filter="*" onClick ={this.handleAction}  >*</button>
              <button data-filter="/" onClick ={this.handleAction}  >/</button>
          </div>
          <div className="row">
              <button data-filter="0" onClick={this.handleType}>0</button>
              <button>save</button>
              <button onClick={this.cancel}>cancel</button>
              <button onClick={this.getResult}>=</button>
          </div>

      </div>
    );
  }
}

ReactDOM.render(<Calculator/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='app'>

Upvotes: 1

Related Questions