React Rendering always behind by one action (Calculator)

Hello,

I am currently working on a calculator and am having issues with my Calculator always being behind by one action.

The code has been stripped down to only basic number inputs so I can demonstrate the issue. The React code is kind of a mess, because I used Codpen’s auto formatting.

Shortened React code here:

class App extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        equationHistory: [], // Not used for now
        currentEquationData: [],
        currentEquationString: "0 + 1 + 1 + 2 + 3 + 5 = 12"
      };
      this.handleClick = this.handleClick.bind(this);
    }
    handleClick(event) {
      let current = event.target.id;
      let numbersCheck = Object.keys(numbers).indexOf(current) !== -1;
      console.log(current);
      if (numbersCheck) {
        this.setState({
          currentEquationData: [...this.state.currentEquationData, numbers[current]]
        });
      }
      this.setState({
        currentEquationString: [...this.state.currentEquationData].join("")
      });
     console.log(this.state)
    }
    render() {
      return (
        <div id="calculator">
          <div id="calculator-display-wrapper">
            <h1>
              Ti-<b>1000X</b>a
            </h1>
            <p id="display">{this.state.currentEquationString}</p>
          </div>
            // here would be the buttons with event handlers for click
         </div>
        )
     }

The react code works by storing the current equation as a array, while also rendering a string format for the display.

When the number button is pushed, the code activates, but the state is not updated before the component renders. If you push another number button, it puts the first number you pushed into the state.

console.log(this.state) shows the state wasn’t even updated directly after setting the state itself with the new value.

Read the docs on setState

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback ( setState(updater, callback) ), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.

As the docs says, for logging, you can either use the callback argument to setState or do the logging inside a componentDidUpdate

this.setState(
  {
    currentEquationString: [...this.state.currentEquationData].join("")
  },
  () => console.log("callback log", this.state)
);
componentDidUpdate() {
  console.log("componentDidUpdate log", this.state);
}
1 Like

Thank you, I rendered my string in the callback of the setState and it updates correctly.