5

From React DOCS:

https://reactjs.org/docs/state-and-lifecycle.html

State Updates May Be Asynchronous

React may batch multiple setState() calls into a single update for performance.

This makes total sense. If you have something like the function below, it would be very inefficient to re-render on every setState call

const [state1,setState1] = useState(false);
const [state2,setState2] = useState(false);
const [state3,setState3] = useState(false);

function handleClick() {
  setState1(true);
  setState2(true);
  setState3(true);
}

So, in the situation above, I expect React to batch all 3 setState calls into a single re-render. And it does exactly that!

But what I want to know is:

Once handleClick has completed, is a re-render guaranteed to happen immediatelly handleClick has done running? I mean literally immediately, like synchronously immediately?

From this snippet that I've build, it seems that this is true. React will synchronously apply the updates (re-render) after handleClick has completed. Correct me if I'm wrong in assuming that.

See snippet below:

  • Click 3 times as fast as you can
  • handleClick will call a setState and will log the current props.counter
  • There is an expensive loop on App, so it will take a long time to re-render
  • You'll be able to click much faster than React can re-render the whole App
  • But you'll see the props.counter is different every time, without any repetition, even if you click multiple times really fast
  • It means that once your 2nd click is processed (it will take a while because of the expensive loop), React has already re-rendered the whole thing and the handleClick function has already been recreated with the new value for props.counter that came from the counter updated state.
  • Try clicking 5 times really fast and you'll see that the behavior is the same.

QUESTION

When setState calls are made inside an event handler function, once that handler function has completed running, is it guaranteed that a re-render will occur immediately (synchronously) after that completion of the handler?

function App() {
  console.log("App rendering...");
  const [counter, setCounter] = React.useState(0);

  // AN EXPENSIVE LOOP TO SLOW DOWN THE RENDER OF 'App'
  
  for (let i = 0; i < 100000; ) {
    for (let j = 0; j < 10000; j++) {}
    i = i + 1;
  }

  return <Child counter={counter} setCounter={setCounter} />;
}

function Child(props) {
  console.log("Child rendering...");
  
  // THIS FUNCTION WILL CALL 'setState'
  // AND WILL ALSO LOG THE CURRENT 'props.counter'
  
  function handleClick() {
    props.setCounter(prevState => prevState + 1);
    console.log(props.counter);
  }

  return (
    <React.Fragment>
      <div>Counter: {props.counter}</div>
      <button onClick={handleClick}>Click</button>
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Community
  • 1
  • 1
cbdeveloper
  • 27,898
  • 37
  • 155
  • 336

2 Answers2

9

Once handleClick has completed, is a re-render guaranteed to happen immediatelly handleClick has done running? I mean literally immediately, like synchronously immediately

For certain events, including click, React guarantees that the component will be re-rendered before another event can occur. (This isn't the case for other events like mousemove.) You can find which events are which here. The current terminology seems to be that DiscreteEvents are the ones for which this guarantee exists, and the UserBlockingEvents are the ones for which it isn't guaranteed. It may well be that in the current implementation that means it's done synchronously at the end of the event handling¹, but I think the guarantee is less specific than that.

I learned this from Dan Abramov on Twitter (he used the older terminology for the events).


¹ Edit: and in fact, in his answer Joseph D. points to a comment by Dan Abramov saying that it is, for now, but also saying "This is implementation detail and may change in future versions".

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Does this mean that when the `setState` is actually called (during the batch that occurs before another event can occur) that this includes the rendering step as well? So `setState` batch call being done by React is like a synchronous call to updating the state AND re-rendering (if necessary)? – Adam Thompson May 27 '20 at 21:08
  • Did you find out the answer to your tweet(s)? Is the `setState` flush guaranteed to occur before the firing event finishes, or just before the next event starts? – Adam Thompson May 27 '20 at 22:24
  • 1
    @AdamThompson - When your code calls `setState`, no, rendering isn't done. It's done afterward, when the call React has made to your component (and other components) is done. My understanding is that that happens at the end of React's handling of the underlying event, but that's an implementation detail. – T.J. Crowder May 28 '20 at 05:48
  • Interesting, so if you use `setState` to change state that controls whether or not a button is disabled or not, it is possible that a user could click twice on that button, but by the time the second event fires the state would be guaranteed to be updated. In this case then, technically would you need to check the state in the event handler (just disabling the button is not _technically_ enough but in practice would likely work based on current implementation details where the state the is flushed and the rendering occurs before the event exits). – Adam Thompson May 28 '20 at 16:41
  • 1
    @AdamThompson - Yeah. But given the amount of code that would break if React's internals changed so that second click came through, I doubt it'll happen, even in concurrent mode. :-) – T.J. Crowder May 28 '20 at 17:28
3

is it guaranteed that a re-render will occur immediately (synchronously) after that completion of the handler?

Yes for onClick.

As pointed by Dan Abramov:

React batches all setStates done during a React event handler, and applies them just before exiting its own browser event handler.

Joseph D.
  • 11,804
  • 3
  • 34
  • 67