3

I've written a test case in Codepen

Run the test by clicking the button, then see the result from Browser Console.

You can see from the console log that even I called setState multiple times before await, it will only update the component once.

But if I call setState multiple times after await, it will update the component multiple times too.

Any idea why is this happening?

Code:

/*
 * A simple React component
 */
class Application extends React.Component {

  state = {
    value: 0
  }

  onClickHandler = (e) => {
    this.runAsyncFunc();
  }

  runAsyncFunc = async() => {
    console.log('BEFORE AWAIT');
    this.setState({ value: 1 });
    this.setState({ value: 1 });
    this.setState({ value: 1 });

    await setTimeout(()=>{}, 2000);

    console.log('AFTER AWAIT');
    this.setState({ value: 1 });
    this.setState({ value: 1 });
    this.setState({ value: 1 });
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('updated');
  }

  render() {
    return <div>
      <p>{this.state.value}</p>
      <button onClick={this.onClickHandler}>RUN TEST</button>
      <p>Please check from Browser Console's Log</p>
    </div>;
  }
}

/*
 * Render the above component into the div#app
 */
React.render(<Application />, document.getElementById('app'));
enno.void
  • 6,242
  • 4
  • 25
  • 42
Cadrick Loh
  • 721
  • 1
  • 7
  • 19

2 Answers2

3

This happens because as this answer states React tries to batch setState calls and process them together when it can. But this is not the case with asynchronous computations because React (and anybody in general) can't predict and reproduce the order of setState's called asynchronously.

So in your case it fallbacks to just updating state 3 times.

Arkadii Berezkin
  • 268
  • 2
  • 12
1

You can got this answer from React.Component life cycle doc (https://reactjs.org/docs/react-component.html)

componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render. React Component Lifecycle

Use this method shouldComponentUpdate(). This method allows your Component to exit the Update life cycle if there is no reason to apply a new render. Out of the box, the shouldComponentUpdate() is a no-op that returns true. This means every time we start an Update in a Component, we will re-render.

I add more code

  shouldComponentUpdate = function(nextProps, nextState) {
    return nextState.value !== this.state.value;
  }

  // Change value will set after await
  runAsyncFunc = async() => {
    console.log('BEFORE AWAIT');
    this.setState({ value: 1 });
    this.setState({ value: 1 });
    this.setState({ value: 1 });

    await setTimeout(()=>{}, 2000);

    console.log('AFTER AWAIT');
    this.setState({ value: 2 });
    this.setState({ value: 2 });
    this.setState({ value: 2 });
  }

Check my Codepen

So if you want to prevent unnecessary render custom method shouldComponentUpdate

Tung Duong
  • 1,156
  • 7
  • 19
  • 1
    I wasn't trying to prevent unnecessary render. I just want to know why after await it will trigger componentDidUpdate() whenever setState is called – Cadrick Loh Sep 24 '18 at 08:55