0

Explanation to why this is not a duplicate: My code is already working, I have included as a comment. The question is why the this context change when I include it to click handler function.
I'm attempting a calculator project in React. The goal is to attach onclick handlers to number buttons so the numbers are displayed on the calculator display area. If the handler is written directly to render method it is working, however, if I'm trying from the ComponentDidMount I get an error this.inputDigit is not a function. How do I bind this.inputDigit(digit) properly?

import React from 'react';
import './App.css';

export default class Calculator extends React.Component {

// display of calculator initially zero
state = {
  displayValue: '0'
}

//click handler function
inputDigit(digit){
  const { displayValue } = this.state;
  this.setState({
    displayValue: displayValue+String(digit)
  })
}

componentDidMount(){

  //Get all number keys and attach click handler function
  var numberKeys = document.getElementsByClassName("number-keys");
  var myFunction = function() {
    var targetNumber = Number(this.innerHTML);
    return this.inputDigit(targetNumber); // This is not working
  };
  for (var i = 0; i < numberKeys.length; i++) {
    numberKeys[i].onclick = myFunction;
  }

}

render() {
  const { displayValue } = this.state;
  return (
    <div className="calculator">
      <div className="calculator-display">{displayValue}</div>
      <div className="calculator-keypad">
        <div className="input-keys">

          <div className="digit-keys">
            {/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}}
            <button className="number-keys">0</button>                
            <button className="number-keys1">1</button>
            <button className="number-keys">2</button>
            <button className="number-keys">3</button>
            <button className="number-keys">4</button>
            <button className="number-keys">5</button>
            <button className="number-keys">6</button>
            <button className="number-keys">7</button>
            <button className="number-keys">8</button>
            <button className="number-keys">9</button>
          </div>
        </div>          
      </div>
    </div>
  )
 }
}
anoop chandran
  • 1,460
  • 5
  • 23
  • 42
  • Possible duplicate of [this.setState is not a function](https://stackoverflow.com/questions/33381756/this-setstate-is-not-a-function) – jmargolisvt Jun 06 '17 at 18:07
  • @jmargolisvt Dude I have expained why it is not a duplicate, Also the error message is different. Could you kindly un-downvote the question? – anoop chandran Jun 06 '17 at 18:27
  • I didn't downvote you. Notice that the answers you are getting are the same as I marked in the duplicate. :) – jmargolisvt Jun 06 '17 at 18:31

2 Answers2

4

Thats because you are writing it inside a function which is not bound,

Use

var myFunction = function() {
    var targetNumber = Number(this.innerHTML);
    return this.inputDigit(targetNumber); 
  }.bind(this);

or

const myFunction = () => {
    var targetNumber = Number(this.innerHTML);
    return this.inputDigit(targetNumber); 
}

After this you need to bind the inputDigit function as well since it also uses setState

//click handler function
inputDigit = (digit) => {
  const { displayValue } = this.state;
  this.setState({
    displayValue: displayValue+String(digit)
  })
}

Since you want to use the button text as well, in that case you should use a separate variable in place of this to call the inputDigit function like

class Calculator extends React.Component {

// display of calculator initially zero
state = {
  displayValue: '0'
}

//click handler function
inputDigit(digit){
  const { displayValue } = this.state;
  this.setState({
    displayValue: displayValue+String(digit)
  })
}

componentDidMount(){

  //Get all number keys and attach click handler function
  var numberKeys = document.getElementsByClassName("number-keys");
  var that = this;
  var myFunction = function() {
    var targetNumber = Number(this.innerHTML);
    console.log(targetNumber);
    return that.inputDigit(targetNumber); // This is not working
  };
  for (var i = 0; i < numberKeys.length; i++) {
    numberKeys[i].onclick = myFunction;
  }

}

render() {
  const { displayValue } = this.state;
  return (
    <div className="calculator">
      <div className="calculator-display">{displayValue}</div>
      <div className="calculator-keypad">
        <div className="input-keys">

          <div className="digit-keys">
            {/*<button className="number-keys" onClick={()=> this.inputDigit(0)}>0</button> This will Work*/}
            <button className="number-keys">0</button>                
            <button className="number-keys">1</button>
            <button className="number-keys">2</button>
            <button className="number-keys">3</button>
            <button className="number-keys">4</button>
            <button className="number-keys">5</button>
            <button className="number-keys">6</button>
            <button className="number-keys">7</button>
            <button className="number-keys">8</button>
            <button className="number-keys">9</button>
          </div>
        </div>          
      </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"></div>
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

Bind it in the constructor

constructor(props) {
    super(props);
    this.inputDigit = this.inputDigit.bind(this);
}
digitake
  • 846
  • 7
  • 16
  • OP is using `"plugins": ["transform-class-properties"] ` to not have to do this. Binding every component method can get messy, that's why they're using `state = ` instead of `this.state` inside a constructor. – Sterling Archer Jun 06 '17 at 18:10
  • `state =` is not in a constructor and you don't need to do that for every single method. Just the one you need to perform callback or any function that subject to context change. – digitake Jun 06 '17 at 18:39