[英]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).在这个硬币翻转程序中,更新的 state.flipResult 和 state.coinURL 变量被打印到控制台有一个延迟(它们总是在 UI 中显示的后面点击)。
I'm sure it has something to do with setState updates being asynchronous.我确定它与异步的 setState 更新有关。 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.例如,我见过将 prevState 传递给 setState 函数以强制更新计数器的示例,但在这种情况下,flipResult 和 coinURL(“头”或“尾”)不是基于先前的状态. 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.
我可以找到一种使用 prevState 在正面和背面之间切换的方法,但将来我可能还希望根据用户的输入或选择“强制”即时状态更新,与之前的状态无关。 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可以在此处找到 Web 应用程序 - https://codesandbox.io/s/flipcoin-zj9sl?file=/src/App.js
setState() does not always immediately update the component.
setState() 并不总是立即更新组件。 It may batch or defer the update until later.
它可能会批量更新或推迟更新。 This makes reading this.state right after calling setState() a potential pitfall.
这使得在调用 setState() 之后立即读取 this.state 成为一个潜在的陷阱。 Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied
相反,请使用 componentDidUpdate 或 setState 回调 (setState(updater, callback)),这两者都保证在应用更新后触发
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.问题是你不理解
setState
是一个异步事件,因此你不能相信调用console.log(this.state.SOMETHING)
会给你新的状态。
the function setState
accepts as second parameter a callback function, so you can do something like this:函数
setState
接受回调函数作为第二个参数,因此您可以执行以下操作:
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;
as updating a state is an async process so we can make the parent function async and wait for updating state by await
command由于更新状态是一个异步过程,因此我们可以使父函数异步并通过
await
命令等待更新状态
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.