asdfgh
asdfgh

Reputation: 11

Why is this React function returning NaN?

I am making a very simple calculator in React, and I am very new to JS and React js.

So, this code just tries to add a few numbers and returns them in H1.

import React, { Component } from "react";

class App extends React.Component {
  state = {
    num1: 0,
    num2: 0,
    answer: 5,
  };

  count = (number) => {
    this.setState({ num1: number });
    return number;
  };

  count2 = (number2) => {
    this.setState({ num2: number2 });
    return number2;
  };

  count3 = () => {
    this.setState({ answer: this.count() + this.count2() });
    console.log(this.state.answer);
    console.log(this.count2());
    return this.state.answer;
  };
  render() {
    return (
      <div>
        <div>
          <h1>{this.state.num1}</h1>
          <button onClick={() => this.count(1)}> 1 </button>
          <button onClick={() => this.count(2)}> 2 </button>
          <button onClick={() => this.count(3)}> 3 </button>
          <button onClick={() => this.count(4)}> 4 </button>
          <button onClick={() => this.count(5)}> 5 </button>
          <button onClick={() => this.count(6)}> 6 </button>
          <button onClick={() => this.count(7)}> 7 </button>
        </div>

        <div>
          <h1>{this.state.num2}</h1>
          <button onClick={() => this.count2(1)}> 1 </button>
          <button onClick={() => this.count2(2)}> 2 </button>
          <button onClick={() => this.count2(3)}> 3 </button>
          <button onClick={() => this.count2(4)}> 4 </button>
          <button onClick={() => this.count2(5)}> 5 </button>
          <button onClick={() => this.count2(6)}> 6 </button>
          <button onClick={() => this.count2(7)}> 7 </button>
        </div>

        <div>
          <button onClick={() => this.count3()}>click</button>

          <h1>{this.state.answer}</h1>
        </div>
      </div>
    );
  }
}

export default App;

So, currently the answer is returning NaN after clicking it. Why is it happening so, and how to fix it?

Upvotes: 0

Views: 1372

Answers (2)

Andy
Andy

Reputation: 63550

Your count methods expect a number passed as an argument but you're not supplying them so state gets updated with undefined in each case, so your answer is NaN.

Have each method simply update the state (no need to return anything from them) and then have count3 update the state with those two numbers.

const { Component } = React;

class App extends React.Component {

  state = { num1: 0, num2: 0, answer: 5 };

  count = (number) => {
    this.setState({ num1: number });
  };

  count2 = (number2) => {
    this.setState({ num2: number2 });
  };

  count3 = () => {
    const { num1, num2 } = this.state;
    this.setState({ answer: num1 + num2 });
  };
  render() {
    return (
      <div>
        <div>
          <h1>{this.state.num1}</h1>
          <button onClick={() => this.count(1)}> 1 </button>
          <button onClick={() => this.count(2)}> 2 </button>
          <button onClick={() => this.count(3)}> 3 </button>
          <button onClick={() => this.count(4)}> 4 </button>
          <button onClick={() => this.count(5)}> 5 </button>
          <button onClick={() => this.count(6)}> 6 </button>
          <button onClick={() => this.count(7)}> 7 </button>
        </div>

        <div>
          <h1>{this.state.num2}</h1>
          <button onClick={() => this.count2(1)}> 1 </button>
          <button onClick={() => this.count2(2)}> 2 </button>
          <button onClick={() => this.count2(3)}> 3 </button>
          <button onClick={() => this.count2(4)}> 4 </button>
          <button onClick={() => this.count2(5)}> 5 </button>
          <button onClick={() => this.count2(6)}> 6 </button>
          <button onClick={() => this.count2(7)}> 7 </button>
        </div>

        <div>
          <button onClick={() => this.count3()}>click</button>

          <h1>{this.state.answer}</h1>
        </div>
      </div>
    );
  }
}
ReactDOM.render(
  <App />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

There is a slightly more concise version of your code that you may find useful. It involves using a loop to add the buttons, and attaching only one listener to the parent div. The listener will catch the button events as they bubble up the DOM, and you can use data attributes from the parent and the button to update the specific state property.

(The even more "React Way" would be identify those parts of your JSX that repeat, and maybe to create reusable Number and NumberGroup components; but that's code for another day.)

const { Component } = React;

class App extends React.Component {

  state = { num1: 0, num2: 0, answer: 0 };

  // Take the group id from the parent dataset,
  // and the id from the button that was clicked,
  // and use that information to update the state
  // Note: in this example, because we're using a
  // data attribute (a string) we need to coerce it
  // to a number
  handleClick = (e) => {
    const { group } = e.target.parentNode.dataset;
    const { id } = e.target.dataset;
    this.setState({ [group]: Number(id) })
  }

  // Return an array of seven buttons each
  // with their own id
  getButtons = () => {
    const buttons = [];
    for (let i = 1; i <= 7; i++) {
      buttons.push(<button data-id={i}>{i}</button>);
    }
    return buttons;
  }

  // When the "Update total" button is clicked
  // get the numbers from state and update the total
  updateTotal = () => {
    const { num1, num2 } = this.state;
    this.setState({ answer: num1 + num2 })
  }

  render() {
    return (
      <div>
        <div data-group="num1" onClick={this.handleClick}>
          <h3>{this.state.num1}</h3>
          {this.getButtons()}
        </div>
        <div data-group="num2" onClick={this.handleClick}>
          <h3>{this.state.num2}</h3>
          {this.getButtons()}
        </div>
        <div class="answer">
          <button onClick={this.updateTotal}>Update total</button>
          <h3>Total: {this.state.answer}</h3>
        </div>
      </div>
    );
  }
}
ReactDOM.render(
  <App />,
  document.getElementById('react')
);
.answer { margin-top: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Upvotes: 3

Erwin Ramadhan
Erwin Ramadhan

Reputation: 402

maybe you can try change your count3 method like this:

count3 = () => {
    this.setState({ answer: this.state.num1 + this.state.num2 });
    return this.state.answer;
  };

then when you click button trigger count3 function to setState answer based on your state.num1 and state.num2 and the answer will be change.

Upvotes: 0

Related Questions