Reputation: 1490
I am working on a calculator app in React. I am facing some weird issues.
The problem lies in the computeResult()
function. The code logic is to push the numbers and operators into the calculationArray
, and when the user clicks the =
button the computeResult()
function is invoked.
The first problem I faced was when the user clicks =
button, the last number keyed in or latest expression
after the last operator, needed to be pushed into the calculationArray
. I found that setState is async, so I made a new array tempcalculationArray
by concatenating the last 'expression' with calculationArray
. Then using a for
loop the total was calculated. The total
was getting logged to the console from inside of the for
loop.
Then the second problem occurred, the console.log
after the for
loop was not working.
The third issue is the setState
after the for
loop is also not working.
This is the code:
const Calculator = React.createClass({
getInitialState() {
return {
expression: "",
calculationArray: [],
answer: 0,
waitingForOperator: true,
};
},
inputDigit(val) {
const { expression, valueHolder, operator, waitingForOperator } = this.state;
this.setState({
expression: expression === "0" ? String(val) : expression + String(val),
waitingForOperator:true
});
},
inputDot() {
const { expression } = this.state;
if (expression.indexOf(".") == -1) {
this.setState({
expression: expression=="" ? "0." :expression + "."
});
}
},
inputSign() {
const { expression } = this.state;
if (expression.charAt(0) == "-") {
this.setState({
expression: expression.substr(1)
});
} else {
this.setState({
expression: "-" + expression
});
}
},
clearAll() {
const { expression , calculationArray, answer} = this.state;
this.setState({
expression: "",
calculationArray: [],
answer: 0
});
},
backSpace() {
const { expression } = this.state;
this.setState({
expression: expression.substr(0, expression.length - 1)
});
},
percent() {
const { expression } = this.state;
this.setState({
expression: String(Number(expression)/100)
});
},
inputOperator(oper) {
const { expression, calculationArray, waitingForOperator} = this.state;
if(waitingForOperator){
this.setState({
calculationArray: calculationArray.concat(expression).concat(oper),
expression:"",
waitingForOperator:false
});
}
},
computeResult() {
const { expression, calculationArray, waitingForOperator, answer} = this.state;
var arithmetic = {
'+': function (x, y) { return x + y },
'-': function (x, y) { return x - y },
'*': function (x, y) { return x * y },
'/': function (x, y) { return x / y },
};
var total = Number(calculationArray[0]);
var tempcalculationArray = calculationArray.concat(expression);
console.log(tempcalculationArray);
for(var i=1; i<=tempcalculationArray.length; i=i+2){
total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
console.log(total);
}
console.log( "why I am not getting printed on console");
this.setState({
calculationArray: calculationArray.concat(expression),
waitingForOperator:false,
answer: total
});
},
render() {
const { expression, answer, math } = this.state;
const { updateExpression, computeResult } = this;
return (
<div>
<div id="displayarea">
<div id="equation"><p>{expression}</p></div>
<div id="answer"><p>{answer}</p></div>
</div>
<div id="calculatorpanel">
<div className="row">
<button className="btn" onClick={() => this.clearAll()}>AC</button>
<button className="btn" onClick={() => this.backSpace()}>←</button>
<button className="btn" onClick={() => this.inputOperator("/")}>÷</button>
<button className="btn" onClick={() => this.inputOperator("*")}>X</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(7)}>
7
</button>
<button className="btn" onClick={() => this.inputDigit(8)}>
8
</button>
<button className="btn" onClick={() => this.inputDigit(9)}>
9
</button>
<button className="btn" onClick={() => this.inputOperator("-")}>-</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(4)}>
4
</button>
<button className="btn" onClick={() => this.inputDigit(5)}>
5
</button>
<button className="btn" onClick={() => this.inputDigit(6)}>
6
</button>
<button className="btn" onClick={() => this.inputOperator("+")}>+</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(1)}>
1
</button>
<button className="btn" onClick={() => this.inputDigit(2)}>
2
</button>
<button className="btn" onClick={() => this.inputDigit(3)}>
3
</button>
<button className="btn" onClick={() => this.percent()}>%</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(0)}>
0
</button>
<button className="btn" onClick={() => this.inputSign()}>±</button>
<button className="btn" onClick={() => this.inputDot()}>.</button>
<button className="btn" onClick={() => this.computeResult()}>=</button>
</div>
</div>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
);
}
});
ReactDOM.render(<Calculator />, document.getElementById("calculator"));
#equation { border: 1px solid #ee82ee; }
#answer { border: 1px solid #00f; }
p { height: 1em; margin: 0; padding: .5em; }
<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="calculator"></div>
Note: Before anyone downvote this question, Please note I have searched thoroughly through the stackflow questions and read most of them, most of the answers do not apply in my scenario. Also, This question cannot be a possible duplicate because I'm looking for a better way to solve this particular problem.
Upvotes: 0
Views: 493
Reputation: 106027
Then the second problem occurred, the
console.log
after the for loop was not working.The third issue is the setState after the for loop is also not working.
I love CodePen, but its console kind of sucks. In particular, it doesn't report errors. If you open your browser's built-in JavaScript console and run your code, you'll see an error like this when you click on the =
button:
Uncaught TypeError: arithmetic[tempcalculationArray[i]] is not a function
This is the cause of both the second and third problems: When an error is thrown, your code stops.
The cause is here:
for(var i=1; i<=tempcalculationArray.length; i=i+2){
total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
// ...
This for
loop will run as long as i
is less than or equal to templcalcuationArray.length
. If i
is equal to the length of the array, it's greater than the index of the last element in the array, and tempcalculationArray[i]
on the next line will return undefined
, and undefined
is not a function.
The solution is to use the <
operator instead of <=
:
for (var i = 1; i < tempcalculationArray.length; i += 2) {
Here it is in a working snippet:
const Calculator = React.createClass({
getInitialState() {
return {
expression: "",
calculationArray: [],
answer: 0,
waitingForOperator: true,
};
},
inputDigit(val) {
const { expression, valueHolder, operator, waitingForOperator } = this.state;
this.setState({
expression: expression === "0" ? String(val) : expression + String(val),
waitingForOperator:true
});
},
inputDot() {
const { expression } = this.state;
if (expression.indexOf(".") == -1) {
this.setState({
expression: expression=="" ? "0." :expression + "."
});
}
},
inputSign() {
const { expression } = this.state;
if (expression.charAt(0) == "-") {
this.setState({
expression: expression.substr(1)
});
} else {
this.setState({
expression: "-" + expression
});
}
},
clearAll() {
const { expression , calculationArray, answer} = this.state;
this.setState({
expression: "",
calculationArray: [],
answer: 0
});
},
backSpace() {
const { expression } = this.state;
this.setState({
expression: expression.substr(0, expression.length - 1)
});
},
percent() {
const { expression } = this.state;
this.setState({
expression: String(Number(expression)/100)
});
},
inputOperator(oper) {
const { expression, calculationArray, waitingForOperator} = this.state;
if(waitingForOperator){
this.setState({
calculationArray: calculationArray.concat(expression).concat(oper),
expression:"",
waitingForOperator:false
});
}
},
computeResult() {
const { expression, calculationArray, waitingForOperator, answer} = this.state;
var arithmetic = {
'+': function (x, y) { return x + y },
'-': function (x, y) { return x - y },
'*': function (x, y) { return x * y },
'/': function (x, y) { return x / y },
};
var total = Number(calculationArray[0]);
var tempcalculationArray = calculationArray.concat(expression);
console.log(tempcalculationArray);
for(var i=1; i<tempcalculationArray.length; i=i+2){
console.log(i, tempcalculationArray[i]);
total = arithmetic[tempcalculationArray[i]](total, Number(tempcalculationArray[i+1]));
console.log(total);
}
console.log( "why I am not getting printed on console");
this.setState({
calculationArray: calculationArray.concat(expression),
waitingForOperator:false,
answer: total
});
},
render() {
const { expression, answer, math } = this.state;
const { updateExpression, computeResult } = this;
return (
<div>
<div id="displayarea">
<div id="equation"><p>{expression}</p></div>
<div id="answer"><p>{answer}</p></div>
</div>
<div id="calculatorpanel">
<div className="row">
<button className="btn" onClick={() => this.clearAll()}>AC</button>
<button className="btn" onClick={() => this.backSpace()}>←</button>
<button className="btn" onClick={() => this.inputOperator("/")}>÷</button>
<button className="btn" onClick={() => this.inputOperator("*")}>X</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(7)}>
7
</button>
<button className="btn" onClick={() => this.inputDigit(8)}>
8
</button>
<button className="btn" onClick={() => this.inputDigit(9)}>
9
</button>
<button className="btn" onClick={() => this.inputOperator("-")}>-</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(4)}>
4
</button>
<button className="btn" onClick={() => this.inputDigit(5)}>
5
</button>
<button className="btn" onClick={() => this.inputDigit(6)}>
6
</button>
<button className="btn" onClick={() => this.inputOperator("+")}>+</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(1)}>
1
</button>
<button className="btn" onClick={() => this.inputDigit(2)}>
2
</button>
<button className="btn" onClick={() => this.inputDigit(3)}>
3
</button>
<button className="btn" onClick={() => this.percent()}>%</button>
</div>
<div className="row">
<button className="btn" onClick={() => this.inputDigit(0)}>
0
</button>
<button className="btn" onClick={() => this.inputSign()}>±</button>
<button className="btn" onClick={() => this.inputDot()}>.</button>
<button className="btn" onClick={() => this.computeResult()}>=</button>
</div>
</div>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
);
}
});
ReactDOM.render(<Calculator />, document.getElementById("calculator"));
#equation { border: 1px solid #ee82ee; }
#answer { border: 1px solid #00f; }
p { height: 1em; margin: 0; padding: .5em; }
<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="calculator"></div>
Upvotes: 2