简体   繁体   中英

Why do the console.logs and UI in my React app not show the same state variable?

In thiscoin flipping program , there is a delay in the updated state.flipResult and state.coinURL variables being printed to console (they are always a click behind what is shown in the UI).

I'm sure it has something to do with setState updates being asynchronous. How then, can I "force" a state update and rerender?

I've seen examples of passing prevState to the setState function in order to force an update to a counter, for instance, but in this instance the flipResult and coinURL ( both "heads" or "tails") are not based on the previous state. I could find a way to toggle between heads and tails using prevState, but I might also want, in future, to "force" an instant state update based on a user's input or selection, unrelated to previous state. Is there a way to do this?

Many thanks in advance for any help!

Here's the code:

import React, { Component } from "react";

import "./styles.css";

class App extends Component {
  state = {
    flipResult: "",
    coinURL: ""
  };

  flipCoin = () => {
    let number = Math.floor(Math.random() * 100) + 1;

    if (number <= 50) {
      this.setState( { flipResult: "heads", coinURL: "heads" });
    } else if (number >= 51) {
      this.setState({ flipResult: "tails", coinURL: "tails" });
    }
    console.log("flipResult is " + this.state.flipResult);
    console.log("flipResult is " + this.state.coinURL);
  };

  render() {
    return (
      <div>
       <h2> Flip a coin! </h2>
        <br />
        <button onClick={this.flipCoin}> Flip! </button>
        <br />

        {this.state.flipResult ? (
          <div>
            The result is {this.state.flipResult}.
            <br />
            <br />
            <img
              src={`./img/${this.state.coinURL}.jpg`}
              alt="coin"
              style={{ width: "200px", height: "auto" }}
            />
          </div>
        ) : (
          <div> No result yet! </div>
        )}
      </div>
    );
  }
}

export default App;


Web app can be found here - https://codesandbox.io/s/flipcoin-zj9sl?file=/src/App.js

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

So you can put console log in callback like this:

  this.setState({ flipResult: "heads", coinURL: "heads" }, () => {
    console.log("flipResult is " + this.state.flipResult);
  });

the problem is that you are not understanding that setState is an asynchronous event, therefore you can not trust that calling console.log(this.state.SOMETHING) will give you the new state.

the function setState accepts as second parameter a callback function, so you can do something like this:

flipCoin = () => {
    let number = Math.floor(Math.random() * 100) + 1;

    if (number <= 50) {
      this.setState( { flipResult: "heads", coinURL: "heads" }, this.logInformation);
    } else if (number >= 51) {
      this.setState({ flipResult: "tails", coinURL: "tails" }, this.logInformation);
    }
    
};

logInformation(){
  console.log("flipResult is " + this.state.flipResult);
  console.log("flipCoin is " + this.state.coinURL);
} 

so after the state has been set, it will call your function and you will be able to see the new state.

NOTE: notice that when you click for the first time it shows empty, on the second click it will show the previous state, this is because as I explained, it is showing the unset value.

the whole code you posted would be the following:

import React, { Component } from "react";

import "./styles.css";

class App extends Component {
  state = {
    flipResult: "",
    coinURL: ""
  };

  flipCoin = () => {
    let number = Math.floor(Math.random() * 100) + 1;

    if (number <= 50) {
      this.setState( { flipResult: "heads", coinURL: "heads" }, this.logInformation);
    } else if (number >= 51) {
      this.setState({ flipResult: "tails", coinURL: "tails" }, this.logInformation);
    }
    
  };

  logInformation(){
    console.log("flipResult is " + this.state.flipResult);
    console.log("flipCoin is " + this.state.coinURL);
  }

  render() {
    return (
      <div>
       <h2> Flip a coin! </h2>
        <br />
        <button onClick={this.flipCoin}> Flip! </button>
        <br />

        {this.state.flipResult ? (
          <div>
            The result is {this.state.flipResult}.
            <br />
            <br />
            <img
              src={`./img/${this.state.coinURL}.jpg`}
              alt="coin"
              style={{ width: "200px", height: "auto" }}
            />
          </div>
        ) : (
          <div> No result yet! </div>
        )}
      </div>
    );
  }
}

export default App;

import React, { Component } from "react";

import "./styles.css";

class App extends Component {
  state = {
    flipResult: "",
    coinURL: ""
  };

  flipCoin = async () => {
    let number = Math.floor(Math.random() * 100) + 1;

    if (number <= 50) {
      await this.setState( { flipResult: "heads", coinURL: "heads" });
    } else if (number >= 51) {
      await this.setState({ flipResult: "tails", coinURL: "tails" });
    }
    console.log("flipResult is " + this.state.flipResult);
    console.log("flipcoin png  is " + this.state.coinURL);
  };

  render() {
    return (
      <div>
       <h2> Flip a coin! </h2>
        <br />
        <button onClick={this.flipCoin}> Flip! </button>
        <br />

        {this.state.flipResult ? (
          <div>
            The result is {this.state.flipResult}.
            <br />
            <br />
            <img
              src={`./img/${this.state.coinURL}.jpg`}
              alt="coin"
              style={{ width: "200px", height: "auto" }}
            />
          </div>
        ) : (
          <div> No result yet! </div>
        )}
      </div>
    );
  }
}

export default App;



What Have I done

as updating a state is an async process so we can make the parent function async and wait for updating state by await command

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM