7
  1. Since React will queue all the setState in event handler to a queue, if there is a large for loop in the event handler, will react wait for the loop?

  2. If setState doesn't happen in an event handler, say in componentDidMount, when it will be flushed?

How does React decide when to flush all the batched updates? What's the strategy?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Blake
  • 7,367
  • 19
  • 54
  • 80

2 Answers2

1

In short, the state updates that should be batched are those that run synchronously. React won't take into account those state updates that run in asynchronous mode, be it inside async/await, .then()/.catch(), setTimeout(), fetch() and similar.

Answering your questions:

  1. Yes, React will wait for the whole loop to finish.
  2. In componentDidMount, the state updates will be batched too until all the synchronous code will be executed.

I suggest you to read the following article to get a better understanding of when batch updates happen in React: https://medium.com/swlh/react-state-batch-update-b1b61bd28cd2

The relevant part:

The main idea is that no matter how many setState calls you make inside a React event handler or synchronous lifecycle method, it will be batched into a single update. That is only one single re-render will eventually happen.

This functionality is relevant for both hooks and regular class components, and its purpose is to prevent unnecessary rendering.

I've tried to prove these statements making my own tests.

This is the component I used to check the batch state updates in event handlers:

export function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    console.time('handleClick');
    for (let i = 0; i < 10000000; i++) {
      setCount((prev) => prev + 1);
    }
    console.timeEnd('handleClick');
  };

  useEffect(() => {
    console.log('render!');
  }, [count]);

  return (
    <div>
      Click count:
      {' '}
      {count}
      <br />
      <button type="button" onClick={handleClick}>Click me!</button>
    </div>
  );
}

This is the result after two consecutive clicks on the button:

Test 1 result - console log

And the tests for batch state updates in componentDidMount:

export class CounterAsClass extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidMount() {
    console.time('componentDidMount');
    for (let i = 0; i < 1000000; i++) {
      this.setState((prevState) => ({ count: prevState.count + 1 }));
    }
    console.timeEnd('componentDidMount');
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        Count:
        {' '}
        {count}
      </div>
    );
  }
}

Results:

Test 2 result - console log

Note: componentDidMount triggers two times in strict mode.

Vasiliy Artamonov
  • 939
  • 2
  • 8
  • 24
0

React uses a simple rule to decide when to flush the batched updates. It waits for the current call stack to be empty for at least _batchedUpdatesExpirationTime milliseconds before flushing the updates.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sparko Sol
  • 651
  • 1
  • 9