I'm absolutely lost with React async setState :(
Here's what I'm trying to achieve: when I click operator button (App.js line 81-98) , I take current(new\updated) state.input, 'format' it with my fixInput function (App.js line 7-22, it takes state.input and event.target.textContent, returns new string), and set the result as new state.input value.
//App.js
function fixInput(inputStr, currentOp) {
const lastTwoOp = /([+\-*/]{2})$/;
const lastThreeOp = /([+\-*/]{3})$/; //
const allowTwo = /(\*|\/)-/; //don't replace *- and /-
//this is not working correctly
if (lastTwoOp.test(inputStr) && !allowTwo.test(inputStr)) {
// if there are 2 operators and those are not the allowed combinations, replace them with the last operator
inputStr = inputStr.slice(0, -2) + currentOp;
} else if (lastThreeOp.test(inputStr)) {
// if there are 3 operators, replace them with the last operator
inputStr = inputStr.slice(0, -3) + currentOp;
} else {
inputStr += currentOp;
}
return inputStr;
}
const init = () => ({
input: "",
output: 0,
showInput: false,
decimal: false,
clickedOp: ""
});
export default class App extends Component {
state = init();
// HANDLERS
handleCeClick = () => {
this.setState(init);
};
handleNumClick = (e) => {
const { input } = this.state;
const value = e.target.textContent;
if (e.target.id === "zero" && +input === 0) {
//if there's 0 in input and 0 is clicked, do nothing
e.preventDefault();
} else {
//add number to input,showinput, reset operators number
this.setState((state) => ({
input: input + value,
showInput: true,
opnum: 0
}));
}
};
handleDecClick = (e) => {
const value = e.target.textContent;
const { input, decimal } = this.state;
if (decimal === true) {
//if there's decimal in the input, do nothing
e.preventDefault();
} else {
//add decimal to the input
if (!input) {
this.setState({
input: "0."
});
} else {
this.setState({
input: input + value
});
}
//default behavior: set decimal to true and show the input
this.setState({
decimal: true,
showInput: true
});
}
};
handleOperClick = (e) => {
const value = e.target.textContent;
const fixedInput = fixInput(this.state.input, value);
this.setState(
(state, props) => {
return {
input: fixedInput,
decimal: false,
showInput: true,
latestOper: value
};
},
() => {
console.log(this.state.input); //current state here i want to use this value in fixInput function and set as new input state:/
}
);
};
handleEqualsClick = () => {
const { input } = this.state;
//todo ? if the last char is oper, delete it
// console.log(input);
let result = +eval(input).toFixed(7).toString();
if (result.length > 11) {
result = result.slice(0, 11);
}
this.setState((state) => ({
showInput: false,
output: result,
input: result //test14
}));
};
render() {
const handlers = {
handleCeClick: this.handleCeClick,
handleNumClick: this.handleNumClick,
handleOperClick: this.handleOperClick,
handleEqualsClick: this.handleEqualsClick,
handleDecClick: this.handleDecClick,
handleInput: this.handleInput
};
const { input, output, showInput } = this.state;
return (
<>
<Display output={output} input={input} showInput={showInput} />
<Keypad {...handlers} />
</>
);
}
}
//Display.js
const Display = (props) => {
//on equals click show the output , if input continues , show input, change showoutput
let { input, output, showInput } = props;
return (
<div id="display" className="display_font">
{showInput ? input : output}
</div>
);
};
//Keypad.js
const Keypad = (props) => {
return (
<div id="keypad">
<button id="clear" onClick={props.handleCeClick}>
CE
</button>
<button id="seven" onClick={props.handleNumClick}>
7
</button>
<button id="eight" onClick={props.handleNumClick}>
8
</button>
<button id="nine" onClick={props.handleNumClick}>
9
</button>
<button id="add" onClick={props.handleOperClick}>
+
</button>
<button id="four" onClick={props.handleNumClick}>
4
</button>
<button id="five" onClick={props.handleNumClick}>
5
</button>
<button id="six" onClick={props.handleNumClick}>
6
</button>
<button id="subtract" onClick={props.handleOperClick}>
-
</button>
<button id="one" onClick={props.handleNumClick}>
1
</button>
<button id="two" onClick={props.handleNumClick}>
2
</button>
<button id="three" onClick={props.handleNumClick}>
3
</button>
<button id="multiply" onClick={props.handleOperClick}>
*
</button>
<button id="zero" onClick={props.handleNumClick}>
0
</button>
<button id="decimal" onClick={props.handleDecClick}>
.
</button>
<button id="equals" onClick={props.handleEqualsClick}>
=
</button>
<button id="divide" onClick={props.handleOperClick}>
/
</button>
</div>
);
};
React setState is async, and it's not updating immediately, so i can access only previous state.input, i can log to console current state from setState callback, and i need to set a new state, which I cannot do from callback. i can't access current state, that is why my fixInput function is not working correctly( press number and repetitive operators, eg 4++, it was expected to allow to add only two combinations of operators *- and /-
I did try to move things around in every way I could think of.
What's wrong with my approach? Is this possible with the structure i've got?
