<p dir="auto">Each state hook update causes a seprate re-render in asynchronous code.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="const sleep = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

export default function App() {
  const [foo, setFoo] = useState(&quot;initial_foo&quot;);
  const [bar, setBar] = useState(&quot;initial_bar&quot;);

  const onAction = useCallback(async () =&gt; {
    await sleep(0); // this is the culprit
    setFoo(&quot;updated_foo&quot;);
    setBar(&quot;updated_bar&quot;);
  }, []);

  console.log(`rendering foo:${foo} bar:${bar}`);

  return (
    &lt;&gt;
      &lt;button onClick={onAction}&gt;run test&lt;/button&gt;
      &lt;div&gt;{&quot;foo: &quot; + foo}&lt;/div&gt;
      &lt;div&gt;{&quot;bar: &quot; + bar}&lt;/div&gt;
    &lt;/&gt;
  );
}"><pre class="notranslate"><code class="notranslate">const sleep = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

export default function App() {
  const [foo, setFoo] = useState("initial_foo");
  const [bar, setBar] = useState("initial_bar");

  const onAction = useCallback(async () =&gt; {
    await sleep(0); // this is the culprit
    setFoo("updated_foo");
    setBar("updated_bar");
  }, []);

  console.log(`rendering foo:${foo} bar:${bar}`);

  return (
    &lt;&gt;
      &lt;button onClick={onAction}&gt;run test&lt;/button&gt;
      &lt;div&gt;{"foo: " + foo}&lt;/div&gt;
      &lt;div&gt;{"bar: " + bar}&lt;/div&gt;
    &lt;/&gt;
  );
}
</code></pre></div>
<p dir="auto">console logs:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="rendering foo:initial_foo bar:initial_bar 
rendering foo:updated_foo bar:initial_bar 
rendering foo:updated_foo bar:updated_bar "><pre class="notranslate"><code class="notranslate">rendering foo:initial_foo bar:initial_bar 
rendering foo:updated_foo bar:initial_bar 
rendering foo:updated_foo bar:updated_bar 
</code></pre></div>
<p dir="auto">React version: 16.13.1</p>
<h2 dir="auto">Steps To Reproduce</h2>
<ol dir="auto">
<li>Follow the codesandbox link</li>
<li>Press 'run test' button</li>
<li>Check the console</li>
</ol>
<p dir="auto">Link to code example:</p>
<p dir="auto"><a href="https://codesandbox.io/s/soextrarerenderswithsleep-3o2g1" rel="nofollow">https://codesandbox.io/s/soextrarerenderswithsleep-3o2g1</a></p>
<h2 dir="auto">The current behavior</h2>
<p dir="auto">Each setState triggers a separate render in asynchronous code, as the console.log indicates.</p>
<h2 dir="auto">The expected behavior</h2>
<p dir="auto">All setState should be merged into a single render, the same, as it happens in synchronous code.</p>
<p dir="auto">Also, this is on <a href="https://stackoverflow.com/questions/63678705/why-each-state-hook-update-causes-a-seprate-re-render-in-asynchronous-code" rel="nofollow">Stack Overflow</a></p>
<p dir="auto"><strong>Do you want to request a <em>feature</em> or report a <em>bug</em>?</strong></p>
<p dir="auto">bug + improvement</p>
<p dir="auto"><strong>What is the current behavior?</strong></p>
<p dir="auto">Sometimes state updates caused by multiple calls to <code class="notranslate">useState</code> setter, are not batched.</p>
<p dir="auto">sandbox1: <a href="https://codesandbox.io/s/8yy0nw2m28" rel="nofollow">https://codesandbox.io/s/8yy0nw2m28</a><br>
sandbox2: <a href="https://codesandbox.io/s/1498n44yr3" rel="nofollow">https://codesandbox.io/s/1498n44yr3</a></p>
<p dir="auto">Step to reproduce: click on <code class="notranslate">increment all</code> text and check the console</p>
<p dir="auto">diff:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// sandbox1
  const incAll = () =&gt; {
    console.log(&quot;set all counters&quot;);
    incA();
    incB();
  };

// sandbox2
  const incAll = () =&gt; {
    setTimeout(() =&gt; {
      console.log(&quot;set all counters&quot;);
      incA();
      incB();
    }, 100);
  };"><pre class="notranslate"><span class="pl-c">// sandbox1</span>
  <span class="pl-k">const</span> <span class="pl-en">incAll</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"set all counters"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">incA</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">incB</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-c">// sandbox2</span>
  <span class="pl-k">const</span> <span class="pl-en">incAll</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-en">setTimeout</span><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
      <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">"set all counters"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-en">incA</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-en">incB</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-c1">100</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">console1:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="render 
set all counters 
set counter 
set counter 
render "><pre class="notranslate"><code class="notranslate">render 
set all counters 
set counter 
set counter 
render 
</code></pre></div>
<p dir="auto">console2:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="render 
set all counters 
set counter 
render 
set counter 
render "><pre class="notranslate"><code class="notranslate">render 
set all counters 
set counter 
render 
set counter 
render 
</code></pre></div>
<p dir="auto"><strong>What is the expected behavior?</strong></p>
<p dir="auto">Render function should be called once after multiple calls of  <code class="notranslate">setState</code></p>
<p dir="auto"><strong>Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?</strong></p>
<p dir="auto">react: 16.7.0-alpha.2</p>