There are two useState hooks used in a component. If we allow for the event loop to "reflow" before modifying the state, each hook causes a separate re-render.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function App() {
  const [foo, setFoo] = useState("initial_foo");
  const [bar, setBar] = useState("initial_bar");
  const onAction = useCallback(async () => {
      await sleep(0); // this is the culprit
      setFoo("updated_foo");
      setBar("updated_bar");
  }, []);
  console.log(`rendering foo:${foo} bar:${bar}`);
  return (
    <>
      <button onClick={onAction}>run test</button>
      <div>{"foo: " + foo}</div>
      <div>{"bar: " + bar}</div>
    </>
  );
}
console output with await sleep(0):
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
If we remove await sleep(0);, everything works as expected: setFoo and setBar cause a single re-render.
console output without await sleep(0):
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
The demo (in the demo console output is duplicated, because it has React strict mode on)
- Why is it happening?
- Is it a bug or a feature?
- How can we prevent intermediate re-renders of this kind?
Update:
Issue has been created in the React project repo
 
     
    