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
Related
Hi Im new to Programming
I tried practicing programming by taking on a simple project: calculator
It works fine but the problem is when I press 3 after 3 it is just showing 3 not 33
how do i work on this ?
My app.js file:
import React, { useState , useEffect, useRef} from "react";
import { add, subtract, multiply, divide } from "../functions.js";
import "../styles.css";
function App() {
const [num, setNum] = useState(0);
const [symbol, setSymbol] = useState("");
const symbols = ["+", "-", "*", "/"];
const numbers = [];
function setButton(event) {
if (Number(prevCount) !== 0 && symbol == "") {
const number = numbers.join("");
setNum(number);
} else {
setNum(event.target.name);
numbers.push(event.target.name);
}
}
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable ...
// ... and can hold any value, similar to an instance property on a class
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
const prevCount = usePrevious(num);
function EqualTo() {
let number = 0;
switch (symbol) {
case "+":
number = add(Number(prevCount), Number(num))
break;
case "-":
number = subtract(Number(prevCount), Number(num));
break;
case "*":
number = multiply(Number(prevCount), Number(num));
break;
case "/":
number = divide(Number(prevCount), Number(num));
break;
}
setNum(number);
setSymbol("");
}
function ac() {
setNum(0);
setSymbol("");
numbers = [];
}
function symbolF(event) {
setSymbol(event.target.name)
let number = 0;
if (prevCount == 0) {
return
} else if (prevCount != 0) {
switch (symbol) {
case "+":
number = Number(num) + Number(prevCount);
setNum(number);
break;
case "-":
number = Number(prevCount) - Number(num);
setNum(number);
break;
case "*":
number = Number(prevCount) * Number(num);
setNum(number);
break;
case "/":
number = Number(prevCount) / Number(num);
setNum(number);
}
} else if (prevCount !== 0 && !(symbol in symbols)) {
const newNum = prevCount + num;
setNum(newNum)
}
}
function forInpute(event) {
setNum(event.target.value);
}
return (
<div>
<br />
<input type="number" placeholder="Enter num" value={num} onChange={forInpute}></input>
<br />
<button onClick={setButton} name="1">1</button>
<span><button name="2" onClick={setButton}>2</button></span>
<span><button name="3" onClick={setButton}>3</button></span>
<span><button name="+" onClick={symbolF}>+</button></span>
<br />
<button name="4" onClick={setButton}>4</button>
<span><button name="5" onClick={setButton}>5</button></span>
<span><button name="6" onClick={setButton}>6</button></span>
<span><button name="-" onClick={symbolF}>-</button></span>
<br />
<button name="7" onClick={setButton}>7</button>
<span><button name="8" onClick={setButton}>8</button></span>
<span><button name="9" onClick={setButton}>9</button></span>
<span><button name="*" onClick={symbolF}>*</button></span>
<br />
<span><button name="0" onClick={setButton}>0</button></span>
<span><button name="." onClick={setButton}>.</button></span>
<span><button name="/" onClick={symbolF}>/</button></span>
<br />
<button onClick={EqualTo}>=</button>
<br />
<button onClick={ac}>AC</button>
</div>
);
}
export default App;
function setButton(event) {
if (Number(prevCount) !== 0 && symbol == "") {
const number = numbers.join("");
setNum(number);
} else {
setNum(event.target.name);
numbers.push(event.target.name);
}
}
I tried implementing this but the condition also doesnt seem to work
I thought and the condition for another digit in the number is when the symbol is not equal to an arithmetic problem
So I even implemented that but it is not seem to work Pls Help ME!
Take the input as string and do string concatenation.
It goes something like this
let text1 = "2";
let text2 = "3";
let result = text1.concat(text2);
result will be "23"
use parseInt before performing operations to convert the string to number.
let a = "100";
let b = parseInt(a);
result will be 100 (integer)
Use num.toString() to convert the output values to string.
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.
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);
};
...