skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

    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


  2. 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;
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search