button click does NOT update this.setState on first click; updates on Second Click [duplicate]

175
June 23, 2019, at 11:40 AM

This question already has an answer here:

  • Can I execute a function after setState is finished updating? 4 answers

I'm trying build my first calculator in React. There seems to be a problem with my buttons (I think). While testing with console.logs, I discovered my first click of the addition button does NOT update all the setStates. Instead, the second click updates it correctly.

ANY idea why it isn't working?

import React, { Component } from 'react';

class calculator extends Component {
    state = { 
        xxxxx:0,
        v1:0,
        v2:0,
        isCal:null
     }
    makeAdd = () => {
        const firstNum = this.state.xxxxx;
        this.setState({
            v1:firstNum,
            xxxxx:0,
            isCal:"add"
        });
        console.log("Add, firstNum is " + firstNum );
        console.log("and v1 is: " + this.state.v1);
        console.log("and isCal is: " + this.state.isCal);
        document.getElementById('DaInserting').innerHTML = null;
    };

    okInsert = (num) => {
        let ss = document.getElementById('DaInserting').innerHTML += num;
        this.setState({xxxxx:ss})
    }
    deAnswer = () => {

        let SecondNum = this.state.xxxxx;
        this.setState({v2:SecondNum})

        console.log("deSecond :" + this.state.v2);
        console.log("this.setState.isCal :" + this.state.isCal);

        let answer = 0;
        this.setState.v2 = this.state.xxxxx;
        if(this.state.isCal==="add") {
            answer = this.state.v1 + this.state.v2;
            console.log("and the answer is :" + answer);
        }
        document.getElementById('DaInserting').innerHTML = answer;
        //this.thenReset();
    }
    thenReset = () => {
        if (this.setState.isCal !== null) {
            this.setState({xxxxx:0})
            this.setState({v1:0})
            this.setState({v2:0})
            this.setState({isCal:null})
        }
    }

    render() {
        //console.log("\nALL states\nthis.state.xxxxx :" + this.state.xxxxx + "\nthis.state.v1 :"+ this.state.v1);
        return (
            <div className="container">
            <div className="row">
                <div style={{height:64}} className="col-12 card-body m-2 shadow-sm rounded bg-light" id="DaInserting"> </div>
            </div>    
            <div className="row">
                <div className="col-3"><div onClick={() => this.okInsert(7)} className="btn btn-secondary btn-lg m-2 btn-block ">7</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(8)} className="btn btn-secondary btn-lg m-2 btn-block ">8</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(9)} className="btn btn-secondary btn-lg m-2 btn-block ">9</div></div>
                <div className="col-3"><div onClick={this.makeDivide} className="btn btn-primary btn-lg m-2 btn-block ">/</div></div>
            </div>
            <div className="row">
                <div className="col-3"><div onClick={() => this.okInsert(4)} className="btn btn-secondary btn-lg m-2 btn-block ">4</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(5)} className="btn btn-secondary btn-lg m-2 btn-block ">5</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(6)} className="btn btn-secondary btn-lg m-2 btn-block ">6</div></div>
                <div className="col-3"><div onClick={this.makeMultiply} className="btn btn-primary btn-lg m-2 btn-block ">x</div></div>
            </div>
            <div className="row">
                <div className="col-3"><div onClick={() => this.okInsert(1)} className="btn btn-secondary btn-lg m-2 btn-block ">1</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(2)} className="btn btn-secondary btn-lg m-2 btn-block ">2</div></div>
                <div className="col-3"><div onClick={() => this.okInsert(3)} className="btn btn-secondary btn-lg m-2 btn-block ">3</div></div>
                <div className="col-3"><div onClick={this.makeSubtract} className="btn btn-primary btn-lg m-2 btn-block ">-</div></div>
            </div>
            <div className="row">
                <div className="col-6"><div onClick={this.deAnswer} className="btn btn-warning btn-lg m-2 btn-block ">=</div></div>
                {/* <div className="col-3"><div onClick={() => this.okInsert(".")} className="btn btn-secondary btn-lg m-2 btn-block ">.</div></div> */}
                <div className="col-3"><div onClick={() => this.okInsert(0)} className="btn btn-secondary btn-lg m-2 btn-block ">0</div></div>
                <div className="col-3"><div onClick={this.makeAdd} className="btn btn-primary btn-lg m-2 btn-block ">+</div></div>
            </div>
            </div>
        );
    }
}
export default calculator;

I have a div called "DaInserting" while gets updated as the user clicks on any number keys, then when the user will click on addition/subtraction/etc, v1 should get updated, but it doesn't. It gets updated on the second click:- check console.log.

After the user has clicked additions/subtraction, "DaInserting" gets a reset, user clicks some numbers again, and then clicks the result button which should then check what the user choose (addition/subtraction)

Answer 1

It tested your code and it is updating the state correctly, remember that setState() is an asynchronous function, so if you wanna check correctly add the function as a callback to be executed after the state is updated, like this:

this.setState({ v1:firstNum, xxxxx:0, isCal:"add"}, () => {
      console.log("Add, firstNum is " + firstNum );
      console.log("and v1 is: " + this.state.v1);
      console.log("and isCal is: " + this.state.isCal);
      console.log(news)
});
Answer 2

In React, setState is asynchronous (see these docs for more on this), so you cannot rely on state updating right after calling setState. Instead, you generally need to wait until the next render for state values to update.

To get around this, you can pass a callback function (as a second argument) to setState, or use await, or use the new values that you're updating state with directly. Here I'd recommend using that last option. Also, it's usually a good idea to only call setState right before exiting a function, to avoid async confusion.

Here is an example how you might update the deAnswer method:

deAnswer = () => {
    let answer = 0;
    if (this.state.isCal === "add") {
        answer = this.state.v1 + this.state.xxxxx;
        console.log("and the answer is :" + answer);
    }
    this.setState({
        v2: this.state.v1,
        answer: answer,
    })
}

render() {
    return (
        <div className="row">
            <div id="DaInserting">{ this.state.answer }</div>
        </div>
    )
}

Note also I've removed the setting of innerHTML, since you can and should change the content of the "DaInserting" div through rendering with state variables, not direct DOM manipulation.

Also consider checking out the Intro to React Tutorial, it's a nice read and will help you in learning and building with React.

Rent Charter Buses Company
READ ALSO
How do I properly set these javascript proxies up?

How do I properly set these javascript proxies up?

I'm trying to set up proxies for the player and opponent characters for this game I'm makingBasically I want the game to print the gameover screen when the player / opponent's health are set to 0

151
How.. get element by class, grab id, and use id as variable to pass to php

How.. get element by class, grab id, and use id as variable to pass to php

I'm running a function on an intervalI need to get element by class name, use the id of each and pass it into a function as a variable

150
Why does my Promise reducer pattern mess up the order?

Why does my Promise reducer pattern mess up the order?

I've implemented my calls to the back-end for this tiny part of my system with Promises using the reduce() pattern as such:

192