Reputation: 401
I am creating a simple calculator with React, and I am trying to add an onClick function to a button. I am using the same technique used in the react tutorial with the tictactoe game, but when I click any button, I just get the following error message:
Uncaught TypeError: this.handleClick is not a function
at onClick (index.js:71)
at HTMLUnknownElement.callCallback (react-dom.development.js:188)
at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
at invokeGuardedCallback (react-dom.development.js:292)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:306)
at executeDispatch (react-dom.development.js:389)
at executeDispatchesInOrder (react-dom.development.js:414)
at executeDispatchesAndRelease (react-dom.development.js:3278)
at executeDispatchesAndReleaseTopLevel (react-dom.development.js:3287)
at forEachAccumulated (react-dom.development.js:3259)
at runEventsInBatch (react-dom.development.js:3304)
at runExtractedPluginEventsInBatch (react-dom.development.js:3514)
at handleTopLevel (react-dom.development.js:3558)
at batchedEventUpdates$1 (react-dom.development.js:21871)
at batchedEventUpdates (react-dom.development.js:795)
at dispatchEventForLegacyPluginEventSystem (react-dom.development.js:3568)
at attemptToDispatchEvent (react-dom.development.js:4267)
at dispatchEvent (react-dom.development.js:4189)
at unstable_runWithPriority (scheduler.development.js:653)
at runWithPriority$1 (react-dom.development.js:11039)
at discreteUpdates$1 (react-dom.development.js:21887)
at discreteUpdates (react-dom.development.js:806)
at dispatchDiscreteEvent (react-dom.development.js:4168)
My code is the following:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Screen extends React.Component {
render() {
return (
<p>99+10</p>
);
}
}
class Operators extends React.Component {
render() {
return (
<div>
{this.props.renderButton('+')}
{this.props.renderButton('-')}
{this.props.renderButton('x')}
{this.props.renderButton('/')}
</div>
);
}
}
class Numbers extends React.Component {
render() {
return (
<div>
<div className="nums-row">
{this.props.renderButton(1)}
{this.props.renderButton(2)}
{this.props.renderButton(3)}
</div>
<div className="nums-row">
{this.props.renderButton(4)}
{this.props.renderButton(5)}
{this.props.renderButton(6)}
</div>
<div className="nums-row">
{this.props.renderButton(7)}
{this.props.renderButton(8)}
{this.props.renderButton(9)}
</div>
<div className="nums-row">
{this.props.renderButton('.')}
{this.props.renderButton(0)}
{this.props.renderButton('=')}
</div>
</div>
);
}
}
function CalculatorButton(props) {
return (
<button onClick={props.onClick} >
{props.text}
</button>
);
}
class Calculator extends React.Component {
handleClick(text) {
console.log(text);
}
renderButton(text) {
return (
<CalculatorButton
onClick= {() => this.handleClick(text)}
text={text}
/>
);
}
render() {
return (
<div className="calculator-container">
<div className="calculator">
<div className="screen">
<Screen />
</div>
<div className="buttons">
<div className="numbers">
<Numbers renderButton={this.renderButton} />
</div>
<div className="operators">
<Operators renderButton={this.renderButton} />
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Calculator />,
document.getElementById('root')
);
This is a picture of the interface, so that it may be easier to understand what I said in the beginning of the question.
Thank you for the help, in advance.
Upvotes: 2
Views: 7378
Reputation: 15
My error was because of my import path was wrong after I rewrite it it works. maybe this can help.
Upvotes: 0
Reputation: 142
You can also set the onclick to null, think is correct to me for <ComboSelect ... onClick={null} />
Upvotes: 0
Reputation: 214
You need to bind the renderButton
function to the Calculator
component instance.
This can be done in several ways:
#1. in the constructor:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.renderButton = this.renderButton.bind(this);
}
/* ... */
}
#2. in the render:
<Numbers renderButton={this.renderButton.bind(this)} />
#3. or by changing the renderButton
method to an arrow function:
renderButton = (text) => {
/* ... */
}
More details along with some pros and cons can be found in the React faq section on functions.
Upvotes: 3
Reputation: 5854
To Solve your issue, just use Arrow function. To do so, please replace two event handler handleClick and renderButton with the following code. It works perfectly.
handleClick = (text) => {
console.log(text);
};
renderButton = (text) => {
return (
<CalculatorButton
onClick={() => this.handleClick(text)}
text={text}
/>
);
};
Upvotes: 1
Reputation: 943207
this.props.renderButton('+')
calls renderButton
immediately and assigns the return value as the event handler.
This is specifically covered in the React documentation:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
The above two lines are equivalent, and use arrow functions and Function.prototype.bind respectively.
In both cases, the e argument representing the React event will be passed as a second argument after the ID. With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.
Upvotes: 1