How to bind onclick handlers to `this` properly on React
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>
)
}
}
Solution 1:
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>