I am relatively new to React/development in general (about 5 months), and I stumbled upon the following problem that has been driving me absolutely crazy. I am wondering if I am being stupid and missing a fundamental concept, or if this is something deeper.
To give you some background on my project and how this problem came up:
I am building a calculator app, very much like the one you get when you search 'calculator' on Google. I am trying to write a function that, every time the user clicks a button on the calculator, saves everything related to the display as an object and adds that object to an array, held in state, of the entire "input history" of the calculator. That way, when the user wants to delete the display values one by one, the calculator can just go back one index in the input history array and go back in time to the previous setting. This is so the calculator can still show the nice dynamically rendered stylings while deleting (exponents, un-matched parentheses, etc.) without having to write a really long, complicated, and almost certainly buggy delete function.
To accomplish this, I am telling my program to, after each change of state (where I keep the display values), add the current settings and display to the input history array. I chose to put this function in the componentDidUpdate method, and ensured it only fires once by only calling it if this.state.inputHistUpdated is false, which happens each time something is added to the display.
Here is my function, inside of the componentDidUpdate() method:
if (!this.state.inputHistUpdated) {
let newInputHist = {
currentMath : this.state.currentMath,
exponentCount : this.state.exponentCount,
spanStr : this.state.spanStr,
rightParensArr : this.state.rightParensArr,
}
console.log(newInputHist);
this.setState((prevState) => {
return {
inputHistory : [...prevState.inputHistory, newInputHist],
inputHistUpdated : true
}
})
}
Each of these value in the input object is potentially impacted by what the user inputs, and appears in some form or another in the display.
The problem is this
The user inputs a 5 into the calculator. newInputHist logs a single object, whose currentMath value is 5. (This is as expected). React DevTools also shows this.state.inputHist to be an array of one object, whose currentMath value is also 5 (as expected). Here is where the problem is: Let's say the user inputs "+" into the calculator. newInputHist logs a single object with a currentMath value of '5 +', which is correct, and a new object has been added to this.state.inputHist whose currentMath value is also '5 +' BUT the currenMath value of the other element in that array, the one we added previously, has also been changed, from '5' to '5 +'. This happens EVERY TIME I update the display; the currentMath property of every single object in the this.state.inputHist array gets updated to the CURRENT currentMath property, instead of saving the one it originally had.
This makes absolutely no sense to me. I've tried other ways of concatenating the object to the array, and have searched up and down Google and SO looking for a solution, but have had trouble finding anything helpful.
Please let me know what you think might be going on here, and if you need more info/code. I wanted to give you enough background, etc. so you can answer my question without adding anything extraneous, but if I didn't feel free to say so.
Here is more of the code:
This function adds the numbers/operations/everything to the display
addToDisplay(input) {
this.setState((prevState) => {
let calcArr = prevState.newCalc ? [] : prevState.currentMath
if (prevState.lastInput=== '□') {
calcArr.pop();
}
if (prevState.lastInput === ' - ' && /[×\-÷\+]/.test(prevState.currentMath[prevState.currentMath.length-2])) {
calcArr[calcArr.length-1] = ' -'
}
calcArr.push(input)
return {
currentMath : calcArr,
calcActive : true,
lastInput : input,
newCalc : false,
inputHistUpdated : false
}
});
}
These two functions deal with the dynamically rendered UI in the display -- adding specially styled right parentheses when the user inputs a left parenthesis, and adding necessary invisible </spans>
when the user up one level in an exponent, which has a different styling
rightParensFunc() {
let styledParens = `<span class='parens exp-${this.state.exponentCount}'>)</span>`;
this.setState((prevState) => {
return {
rightParensArr : [styledParens, ...prevState.rightParensArr],
inputHistUpdated : false
}
});
}
spanFunc() {
let counter = this.state.exponentCount;
let spans = '';
while (counter > 0) {
spans = spans + '</span>';
counter--;
};
this.setState({
spanStr : spans,
inputHistUpdated : false
})
}
And then here is my constructor function
class CalcBody extends React.Component {
constructor(props){
super(props);
this.state = {
calcActive : false,
angleUnits : 'radians',
historyVisible : false,
inverse : false,
prevTotal : '0',
lastInput : '0',
currentMath : [],
exponentCount : 0,
prevMath : '',
rightParensArr : [],
spanStr : '',
calcHistory : [],
inputHistory : [],
inputHistUpdated : true,
newCalc : true
};