如何解释React.js中setState的同步行为?

huangapple go评论75阅读模式
英文:

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 &quot;react&quot;;

const App = () =&gt; {
  const [state, setState] = useState(&quot;start value&quot;);

  const run = () =&gt; {
    console.log(&quot;before setState: &quot; + state);
    setState(&quot;new value&quot;);
    console.log(&quot;after setState: &quot; + state);
  };

  const asyncro = async () =&gt; {
    let promise = new Promise((resolve) =&gt; {
      setTimeout(resolve, 1000);
    });
    return promise;
  };

  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Hello CodeSandbox&lt;/h1&gt;
      &lt;h2&gt;Start editing to see some magic happen!&lt;/h2&gt;
      &lt;button
        onClick={async () =&gt; {
          await asyncro();
          run();
        }}
      &gt;
        click
      &lt;/button&gt;
    &lt;/div&gt;
  );
};
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

huangapple
  • 本文由 发表于 2023年8月9日 01:53:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76862068.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定