英文:
How to explain this synchronous behaviour of setState in react.js?
问题
在这段React.js代码中,点击按钮会改变状态,但是setState的执行似乎是同步的。
为什么输出结果是这样的?
before setState: start value
after setState: new value
如果我删除
await this.asyncro();
输出结果就是预期的结果,因为setState不是同步的。
before setState: start value
after setState: start value
英文:
In this code in react.js a button click change the state, but the execution of setState seems to be synchronous
https://codesandbox.io/s/setstate-synchronous-9sctlp
import React from "react";
class App extends React.Component {
state = {
data: "start value"
};
do = () => {
console.log("before setState: " + this.state.data);
this.setState({ data: "new value" });
console.log("after setState: " + this.state.data);
};
asyncro = async () => {
let promise = new Promise((resolve) => {
setTimeout(resolve, 1000);
});
return promise;
};
render() {
return (
<div className="App">
<button
onClick={async () => {
await this.asyncro();
this.do();
}}
>
click
</button>
</div>
);
}
}
export default App;
Why the output is?
before setState: start value
after setState: new value
And if I delete
await this.asyncro();
the output is the expected result because setState is not syncronous
before setState: start value
after setState: start value
答案1
得分: 2
当您使用函数组件(和钩子)时,您会得到预期的结果,而且只有预期的结果。
社区之所以转向使用函数组件而不是类组件的原因之一是,类组件往往表现得不可预测,并且可能与函数作用域和状态产生不明显的交互,正如您的问题所示。
以下是将您的示例重写为函数组件的代码,注意它的行为与预期一致:
import React, { useState } from "react";
const App = () => {
const [state, setState] = useState("start value");
const run = () => {
console.log("before setState: " + state);
setState("new value");
console.log("after setState: " + state);
};
const asyncro = async () => {
let promise = new Promise((resolve) => {
setTimeout(resolve, 1000);
});
return promise;
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button
onClick={async () => {
await asyncro();
run();
}}
>
click
</button>
</div>
);
};
export default App;
英文:
When you use functional components (and hooks) as recommended by the react team, you get the expected result and ONLY the expected result.
One of the reasons the community has made the switch to functional components instead of class components is that class components have a tendency to behave unpredictably and can have non-obvious interactions with function scope and state, as evident by your question.
Here is your example rewritten as a functional component, notice it behaves as expected:
import React, { useState } from "react";
const App = () => {
const [state, setState] = useState("start value");
const run = () => {
console.log("before setState: " + state);
setState("new value");
console.log("after setState: " + state);
};
const asyncro = async () => {
let promise = new Promise((resolve) => {
setTimeout(resolve, 1000);
});
return promise;
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button
onClick={async () => {
await asyncro();
run();
}}
>
click
</button>
</div>
);
};
export default App;
答案2
得分: 1
终于我找到了答案,多亏了Dan Abramov。
> 如果你不在事件处理程序内部,setState() 目前是同步的。
https://twitter.com/dan_abramov/status/949992957180104704
然后,因为 setState 在一个带有将来要调用的内容的 async/await 块内部,所以 setState 是同步的。
重要提示:这种行为在 React 18 版本中已经发生了变化,不再出现这种行为。
https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching
英文:
Finally I found the answer thanks to Dan Abramov
> setState() is currently synchronous if you're not inside an event
> handler.
https://twitter.com/dan_abramov/status/949992957180104704
Then because setState is inside of a async/await block with something to call in the future then setState is synchronous
IMPORTANT: that behavior has changed in react version 18 and this behavior do not happen any more.
https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论