am trying to create a calculator in react but when i do the calulaculations it gives me that syntax Error. For example when calaculate different numbers day (30 + 23423 - 3428 *5235) it gives me that error. also i cant get my Reset and backspace and decimal to work. Can someone please help
here is my code
import React, { Component } from 'react'
import Button from "./components/Button";
import Input from "./components/Input";
import {buttonData} from "./components/Data"
class App extends Component {
constructor(props) {
super(props)
this.state = {
input: [],
result: 0,
operator: "+"
}
}
calculate = (num1, operator, num2) => {
let result;
// console.log("math");
if(!isNaN(num2)) {
result = eval(num1 + operator + num2)
this.setState({result})
}
}
handleClick = (button) => {
let input = [...this.state.input]
let result = this.state.result;
let operator = this.state.operator;
const num = parseFloat(input.join(''))
switch(true) {
case /[+-\/\*]/.test(button):
// console.log(operator);
this.calculate(result, operator, num)
operator = button;
input = operator;
break
case/\d/.test(button):
// console.log(button);
if(/[+-\/\*]/.test(button)){
input = []
}
input.push(button)
break
}
if(button === "="){
this.calculate(result, operator, num)
}
else if(button === "C"){
this.reset()
}
else if(button === "CE"){
this.backspace()
}
else if(button === "."){
this.handleDecimal()
}
this.setState({input, operator})
};
reset = () => {
this.setState({
input: "0",
reset: []
})
};
backspace = () => {
this.setState({
input: this.state.input.slice(0, -1)
})
};
handleDecimal = () => {
const { input } = this.state;
// ! add the point only if no point is already included
if(!/\./.test(input)) {
this.setState({input: `${input}.`})
}
}
render() {
return (
<div className="app">
<div className="calc-wrapper">
<h2>Tony's Calculator</h2>
<div id="display" >
<Input input={this.state.input}
result={this.state.result}
/>
</div>
{buttonData.map((item) => {
let name = item.button;
let id = item.id;
return <Button
key={id}
name={name}
id={id}
value={name}
handleClick={this.handleClick}
/>
} )
}
</div>
</div>
)
}
}
export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter link description here
The error happens when you try to use eval() or alert() with a mathematical expression that contains -- or ++
Ex:
alert(-50--30.25*32)
will throw: Uncaught SyntaxError: invalid increment/decrement operand
So, you need to surround your value by parenthesis or replace your signs with a single sign.
Ex:
alert(-50-(-30.25)*32)
Or:
str.replace(/[-]{2}|[+]{2}/g, '+')
I would use parenthesis if I was constructing a string and regex if I was reading a string.
Related
I have made a basic calculator in React using the reducer hook, however, there are two issues arising:
I have the state as {prevOperand,currentOperand,operation}
In the case where two operators are clicked consecutively, I want to check if the last character in the string in the currentOperand contains any of the arithematic operators so that I can return the same state back (preventing the second selected operator from entering). However, the .match method doesn't seem to be working, and the return statement in the 'if' loop isn't getting executed. What might be the problem here?
I have used a function for evaluating the numbers entered in currentOperand, which returns the value of the varible "result", which is equated to the currentOperand.Afterwards, when I press any operation (or) the DEL (DELETE) button , I am getting an error. How to resolve this????
import { ACTIONS } from "./Actions";
var symbol ;
function reducer (state, action) {
symbol = ['*','.','+','-','/']
console.log(state)
console.log(action)
switch(action.type){
case ACTIONS.ADD_DIGIT: //add digit
return {...state, currentOperand: `${state.currentOperand || ""}${action.payload}`}
case ACTIONS.DELETE_DIGIT: //single digit
return {...state, currentOperand: state.currentOperand.substring(0,state.currentOperand.length-1)}
case ACTIONS.CLEAR: //clear output
return { prevOperand: null, currentOperand: 0 ,operation: null}
case ACTIONS.CHOOSE_OPERATION:
if(action.payload && state.currentOperand == null) { // operation and evaluation shouldn't be the first button pressed
return state
} else if(state.currentOperand.includes('/') || state.currentOperand.includes('*') ||
state.currentOperand.includes('+')|| state.currentOperand.includes('-')) {
// CHECK FOR THE ERROR FROM HERE ON --------------------------------------
let string = state.currentOperand
let variable = '/^[*+-/]*$/'
console.log(string)
if(string.charAt([string.length - 1]).match(variable)) {
console.log("operator present")
return state // FIRST RETURN STATEMENT -------------------
}
const numbers = state.currentOperand.split(/([-+*/])/g) // the number is evaluated
const result = evaluateNum(numbers[0], numbers[1],numbers[2])
return {currentOperand : result} //SECOND RETURN STATEMENT -------------------
} else if(action.payload ==='.' && state.currentOperand.includes('.')){ //extra decimals
return state
} else{ //displaying the operations
return {...state, currentOperand: `${state.currentOperand}${action.payload}`}
}
case ACTIONS.EVALUATION: //calling evaluation function (when = is pressed)
const numbers = state.currentOperand.split(/([-+*/])/g) // the number is evaluated
const result = evaluateNum(numbers[0], numbers[1],numbers[2])
return {currentOperand : result} // AFTER THIS COMMAND, UNABLE TO USE ANY ARTHEMATIC OPERATORS OR
//DELETE BUTTON
default:
return state
}
}
----------------------------UPTO HERE ------------------------------------------------------------
// evaluation function
function evaluateNum( prevOperand, operation, currentOperand){
let prevNum = parseFloat(prevOperand)
let currentNum = parseFloat(currentOperand)
console.log(prevNum)
console.log(currentNum)
if (operation === '+'){
return prevNum + currentNum
} else if(operation === '-') {
return prevNum - currentNum
} else if(operation === '*') {
return prevNum * currentNum
} else if(operation === '/' && currentOperand!= '0') {
return prevNum / currentNum
}
}
export default function App() {
const [{prevOperand=null, currentOperand =0, operation=null}, dispatch] = useReducer(reducer,{})
return (
<div className="calculator-grid">
<div className="output">
<div className="prev-operand">{prevOperand} {operation}</div>
<div className="current-operand">{currentOperand}</div>
</div>
<button className="span-two" onClick={()=> {
dispatch({type: ACTIONS.CLEAR , payload:null})
}}>AC</button>
<button onClick={()=> {
dispatch({type: ACTIONS.DELETE_DIGIT , payload:null})
}}>DEL</button>
<ButtonOperation operation='/' dispatch={dispatch}/>
<ButtonDigit number="1" dispatch={dispatch}/>
<ButtonDigit number="2" dispatch={dispatch}/>
<ButtonDigit number="3" dispatch={dispatch}/>
<ButtonOperation operation='*' dispatch={dispatch}/>
<ButtonDigit number="4" dispatch={dispatch}/>
<ButtonDigit number="5" dispatch={dispatch}/>
<ButtonDigit number="6" dispatch={dispatch}/>
<ButtonOperation operation='+' dispatch={dispatch}/>
<ButtonDigit number="7" dispatch={dispatch}/>
<ButtonDigit number="8" dispatch={dispatch}/>
<ButtonDigit number="9" dispatch={dispatch}/>
<ButtonOperation operation='-' dispatch={dispatch}/>
<ButtonOperation operation='.' dispatch={dispatch}/>
<ButtonDigit number="0" dispatch={dispatch}/>
<button className="span-two" onClick={()=> {
dispatch({type: ACTIONS.EVALUATION , payload:'='})
}}>=</button>
</div>
);
}
</code>```
THE ACTIONS OBJECT PASSED INTO THE REDUCER ---->
CHOOSE_DIGIT: ADDING DIGIT TO STRING/DISPLAY
DELETE_DIGIT: REMOVE THE LAST CHARACTER ENTERED
CLEAR: EMPTY THE DISPLAY AND VARIABLES
CHOOSE_OPERATION: SELECT *, /,+,-, . (DECIMAL)
EVALUATION: PRESS = SIGN
I am trying to set the maximum length of an input through js because I need to do it like this, and in part it already works, I need it to print with commas but not to count them, this is my code:
<input onInput={ (evt) => this.handleChange(evt)} />
if the maxlenght is 8 then until I press 8 numbers it prints me like this:
12,222,222
but if I press one more number, 8 are still preserved but the format is damaged and it prints like this:
12222222
private handleChange(evt) {
evt.target.value=evt.target.value.replace(/,/g,"").slice(0, this.maxLength)
const element = evt.target ? evt.target.value : '';
if (this.type == 'decimal') {
const value= this.formatDecimal(element,this.maxLength, this.maxDecimals, this._separatorDecimal, this.separator);
}
}
formatDecimal(input, maxLength, maxDecimals, separator, separatordigit) {
var input_val = input;
if (input_val === separator) {
input_val = '';
}
input_val = this.formatNumber(input_val, maxLength, separatordigit);
input_val = "" + input_val;
}
return input_val;
}
I hope you can help me, thanks
The logic seems to be working when I made the input controlled and made value part of the state of the component.
Main things to note are:
You shouldn't mutate the input's value directly. That could cause some issues.
You should make value changes that you expect to be related to the DOM in component state rather than component properties. Properties won't cause the component to re-render and sync to the dom.
class Example extends React.Component {
maxLength = 14;
type = 'decimal';
state = { value: '' };
separator = ','
handleChange = (ev) => {
const element = ev.target.value.replace(/,/g,'').slice(0,this.maxLength);
if (this.type == 'decimal') {
this.setState({value:this.formatDecimal(
element,
this.maxLength,
this.maxDecimals,
this._separatorDecimal,
this.separator
)});
}
};
formatDecimal(input, maxLength, maxDecimals, separator, separatordigit) {
var input_val = input;
if (input_val === separator) {
input_val = '';
}
input_val = this.formatNumber(input_val, maxLength, separatordigit);
input_val = '' + input_val;
return input_val;
}
formatNumber(n, maxLength, separator) {
n = n.replace(/\D+/g, '');
n = n.substring(0, maxLength);
// format number 1000000 to 1,234,567
return n.replace(/\D+/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, separator);
}
render() {
return (
<div>
<input value={this.state.value} onChange={this.handleChange} />
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
In a Javascript Calculator being built with React, there is a switch statement that is intended to return a function. It appears that switch statements are allowed to return if they are in a function. However, there is an error when returning the function from the switch statement: TypeError: func is not a function.
Any help would be greatly appreciated.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
class JavascriptCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 0,
array: [],
}
this.display = this.display.bind(this);
this.clear = this.clear.bind(this);
this.calculate = this.calculate.bind(this);
}
calculate(){
let regex = /[*/+‑]/;
let text = '';
let length = this.state.array.length;
let operators = [];
//console.log(this.state.array);
// capture numbers longer than one digit by adding them to a string
// add a comma in place of the operators so the string
// can be split into an array.
for (let i = 0; i < length; i++){
if (this.state.array[i].match(/\d/)) {
text+=this.state.array[i];
}
if (this.state.array[i].match(regex)){
text+=',';
// put the operators in their own array
operators.push(this.state.array[i]);
}
if (this.state.array[i] == '='){
break;
}
}
console.log(operators);
let numbers = text.split(',');
console.log(numbers);
let answer = 0;
let func = undefined;
// use the numbers array and the operators array
// to calculate the answer.
for (let i = 0; i < numbers.length; i++){
func = returnFunc(operators.shift());
console.log(func);
answer = func(answer, numbers[i]);
}
function returnFunc(val) {
switch (val) {
case '+':
return function sum(a,b) { return a+b};
case '-':
return function subtract(a,b) { return a-b};
case '*':
return function multiply(a,b) { return a*b};
case '/':
return function divide(a,b) {return a/b};
}
}
}
display(text){
// if display is zero, remove the leading zero from the text.
if(this.state.text == 0){
this.state.text = '';
}
let displayed = this.state.text += text;
this.setState({ text: displayed});
// push the characters onto an array
// an array is used to isolate the math operators to
// associate them to a function
if (text != '='){
this.state.array.push(text);
}
// if text ends in equals sign, run the calculate function.
if (text == '='){
this.state.array.push(text);
this.calculate();
}
}
clear(id){
this.setState({ text: id });
this.setState({ array: [] });
this.setState({ indices: [] });
this.setState({ number: [] });
}
render() {
return (
<div id="javascript-calculator">
<h1 id="title">Javascript Calculator</h1>
<div id="display">{this.state.text}</div>
<hr/>
<div>
<button id="clear" onClick={e => this.clear("0")}> clear </button>
<button id="equals" onClick={e => this.display("=")}> = </button>
<button id="zero" onClick={e => this.display("0")}> 0 </button>
<button id="one" onClick={e => this.display("1")}> 1 </button>
<button id="two" onClick={e => this.display("2")}> 2 </button>
<button id="three" onClick={e => this.display("3")}> 3 </button>
<button id="four" onClick={e => this.display("4")}> 4 </button>
<button id="five" onClick={e => this.display("5")}> 5 </button>
<button id="six" onClick={e => this.display("6")}> 6 </button>
<button id="seven" onClick={e => this.display("7")}> 7 </button>
<button id="eight" onClick={e => this.display("8")}> 8 </button>
<button id="nine" onClick={e => this.display("9")}> 9 </button>
<button id="add" onClick={e => this.display("+")}> + </button>
<button id="subtract" onClick={e => this.display("-")}> - </button>
<button id="multiply" onClick={e => this.display("*")}> * </button>
<button id="divide" onClick={e => this.display("/")}> / </button>
<button id="decimal" onClick={e => this.display(".")}> . </button>
</div>
</div>
);
}
}
ReactDOM.render(<JavascriptCalculator />, document.getElementById("app"));
index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Javascript Calculator</title>
<style>
</style>
</head>
<body>
<main>
<div id="app"></app>
</main>
</script>
</body>
</html>
You try to use an operator for every number in the input text. But usually there will be more numbers than operators, so this will fail for the last number that doesn't have an operator associated.
The returnFunc function will then be called with undefined because the operators array is empty. And will in this case also return undefined instead of a function.
You start with answer = 0 and then do the first operation. Instead, start with answer being the first number and then do the first operation with the second number and so forth
// initialize answer with first number
let answer = numbers[0];
let func = undefined;
// Start with second number
for (let i = 1; i < numbers.length; i++){
func = returnFunc(operators.shift());
console.log(func);
answer = func(answer, numbers[i]);
}
this.display(answer);
If you have a switch statement, it is always good to have a default case. If there is no other way to handle an unknown input, at least throw an exception so that you can identify that the switch is called with an invalid input.
function returnFunc(val) {
switch (val) {
case '+':
// Error here because a and b are strings.
// Convert them to numbers first to add their values *
return function sum(a,b) { return a+b};
case '-':
return function subtract(a,b) { return a-b};
case '*':
return function multiply(a,b) { return a*b};
case '/':
return function divide(a,b) {return a/b};
default: // <- Use default case
throw new Error("Called with unknown operator " + val);
}
}
*) See How to add two strings as if they were numbers?
Blockquote I CAN NOT PUSH MY RESULT TO THE PAGE, BUT new1Num and new2Num as well as newOper is
Blockquote reflected just fine. result only shows up inside console.log(result); I already tried taking var result outside of main function and tried const and also tried "let [result, setNewAnsw] = useState(0); " just like i did for new1Num but still no luck.
import React, { useState } from 'react';
// If the component accepts a parameter, this is referred to as a "prop."
// Props allows to pass values INTO my components from a parent document / component.
function Calculator (props)
{ // Every component should return JSX.
const [new1Num, setNew1Num] = useState(0);
const [new2Num, setNew2Num] = useState(0);
const [newOper, setNewOper] = useState('add');
// let [result, setNewAnsw] = useState(0);
var result;
const onFormSubmit = event =>
{
event.preventDefault();
console.log(new1Num);
const a = parseFloat(new1Num);
console.log(newOper);
console.log(new2Num);
const b = parseFloat(new2Num);
let result;
if (newOper == 'add'){
result = a + b;
console.log(result);
}
else if (newOper == 'subtract'){
result = a - b;
}
else if (newOper == 'divide' && b !=0){
result = a / b;
}
else if (newOper == 'multiply'){
result = a * b;
}
return newResult = result; //IN THIS SECTION I CAN'T TAKE MY RESULT TO RENDER INSIDE DOM
}
let heading = props.heading;
let input1 = props.input1;
let input2 = props.input2;
let newResult = props.newResult;
return (
<div>
<h1> { heading } </h1>
<form onSubmit = {onFormSubmit}>
<h2> { input1 } </h2>
<input
type="number" min="0" step="1"
onChange={e => { setNew1Num( e.target.value ) }}
value={new1Num}
className='field' />
<h2>Operator</h2>
<select
onChange={e => { setNewOper( e.target.value ) }}
value={newOper}
className='oper' >
<option value='add'>+</option>
<option value='subtract'>-</option>
<option value='divide'>/</option>
<option value='multiply'>*</option>
</select>
<h2> {input2}</h2>
<input
type="number" min="0" step="1"
onChange={e => { setNew2Num( e.target.value ) }}
value={new2Num}
className='field' />
<h2></h2>
<input type='submit' className='submitBtn success' value='Submit'/>
<h3>RESULT:
{new1Num}
{newOper}
{new2Num}
{newResult}
</h3>
</form>
</div>
);//new1Num and newOper and new2Num are populating inside browser just fine
}
export default Calculator
In order to publish the result to the page react would need to rerender. This can be achieved by saving the result into state and updating when completing a computation.
Use const [result, setNewAnsw] = useState(0); to set/access the result, and setNewAnsw(result) in the form's onFormSubmit callback. Delete let newResult = props.newResult; as newResult isn't passed as a prop, it's computed in state. Also, use "===" vs "==" for comparisons.
...
const [result, setNewAnsw] = useState(0);
const onFormSubmit = (event) => {
event.preventDefault();
console.log(new1Num);
const a = parseFloat(new1Num);
console.log(newOper);
console.log(new2Num);
const b = parseFloat(new2Num);
let result;
if (newOper === "add") {
result = a + b;
console.log(result);
} else if (newOper === "subtract") {
result = a - b;
} else if (newOper === "divide" && b) { // b any value other than 0 is truthy!
result = a / b;
} else if (newOper === "multiply") {
result = a * b;
}
setNewAnsw(result);
};
...
I have code that has a user input numbers that are stored in an array. I would like to know how to prevent a user from entering a negative number in the array and showing in red (preferably red text below input) that they can't enter a negative number
I was able to get the array to identify what type of integer is put into an array with an if... else statement, but I can't seem to get the array to pop off the negative number.
<input type="number" id="user_input">
<button type="button" id="myBtn">Click to store</button>
<script>
const myArray = [];
const addData = () => {
let inputData = document.getElementById('user_input');
myArray.push(parseInt(inputData.value));
console.log(myArray);
if (inputData.value <= 0) {
console.log("negative int")
} else if (inputData.value == 0) {
console.log('nothing entered')
} else {
console.log("positive int")
}
inputData.value = "";
}
document.getElementById('myBtn').addEventListener('click', addData);
</script>
You could take the numerical value and check if the string is given or if this value is an integer value, or negative. Then you need function for showing a wanted message with a certain color.
This solution features an exit early, exit often paradigm, where the function takes a condition and exits if the condition is met, to prevent to go to the natural end of the function by using a chained else ... if ... else structure.
In short, this approach works without else parts, which is here possible.
const
myArray = [],
addData = () => {
const setMessage = (string, color = '#000000') => {
message.style.color = color;
message.innerHTML = string;
}
let inputData = document.getElementById('user_input'),
message = document.getElementById('message'),
value = +inputData.value;
if (inputData.value === '') {
setMessage('nothing entered', '#bb0000');
return;
}
inputData.value = "";
if (value !== Math.floor(value)) {
setMessage('no int', '#bb0000');
return;
}
if (value <= 0) {
setMessage('negative int', '#bb0000');
return;
}
myArray.push(value);
console.log(myArray);
setMessage('positive int');
}
document.getElementById('myBtn').addEventListener('click', addData);
<input type="number" id="user_input">
<button type="button" id="myBtn">Click to store</button>
<p id="message"></p>
The issue here is that you're pushing the value in the array before checking for its type. You just have to do it like this to prevent negative numbers to get inside the array :
const myArray = [];
const addData = () => {
let inputData = document.getElementById('user_input');
console.log(myArray);
if (inputData.value <= 0) {
console.log("negative int")
} else if (inputData.value == 0) {
console.log('nothing entered')
} else {
console.log("positive int")
myArray.push(parseInt(inputData.value));
}
inputData.value = "";
}
document.getElementById('myBtn').addEventListener('click', addData);
Or, very simply <input type="number" id="user_input" min="0">