<p dir="auto">React version: 16.13.1</p>
<h2 dir="auto">Steps To Reproduce</h2>
<ol dir="auto">
<li>I used the following code in Jest</li>
</ol>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="  it(&quot;should not rerender when setting state to the same value via click&quot;, async () =&gt; {
    const callback = jest.fn();
    function MyComponent() {
      const [foo, setFoo] = useState(&quot;bir&quot;);
      callback();
      return (&lt;div data-testid=&quot;test&quot; onClick={() =&gt; setFoo(&quot;bar&quot;)}&gt;{foo}&lt;/div&gt;);
    }

    const { getByTestId } = render(&lt;MyComponent /&gt;)
    const testElement = getByTestId(&quot;test&quot;);
    expect(testElement.textContent).toEqual(&quot;bir&quot;);
    expect(callback).toBeCalledTimes(1);

    act(() =&gt; { fireEvent.click(testElement); });
    expect(testElement.textContent).toEqual(&quot;bar&quot;);
    expect(callback).toBeCalledTimes(2);

    act(() =&gt; { fireEvent.click(testElement); });
    expect(testElement.textContent).toEqual(&quot;bar&quot;);
    expect(callback).toBeCalledTimes(2); // gets 3 here

    /*
     assuming we update the last line as follows
     expect(callback).toBeCalledTimes(3); // really should be 2
     act(() =&gt; { fireEvent.click(testElement); });
     expect(testElement.textContent).toEqual(&quot;bar&quot;);
     expect(callback).toBeCalledTimes(3); // does not re-render
    */

  })"><pre class="notranslate">  <span class="pl-s1">it</span><span class="pl-kos">(</span><span class="pl-s">"should not rerender when setting state to the same value via click"</span><span class="pl-kos">,</span> <span class="pl-k">async</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-k">const</span> <span class="pl-s1">callback</span> <span class="pl-c1">=</span> <span class="pl-s1">jest</span><span class="pl-kos">.</span><span class="pl-en">fn</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">function</span> <span class="pl-smi">MyComponent</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-k">const</span> <span class="pl-kos">[</span><span class="pl-s1">foo</span><span class="pl-kos">,</span> <span class="pl-s1">setFoo</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-en">useState</span><span class="pl-kos">(</span><span class="pl-s">"bir"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-en">callback</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
      <span class="pl-k">return</span> <span class="pl-kos">(</span><span class="pl-kos">&lt;</span><span class="pl-smi">div</span><span class="pl-kos"></span> <span class="pl-s1">data</span><span class="pl-c1">-</span><span class="pl-s1">testid</span><span class="pl-c1">=</span><span class="pl-s">"test"</span> <span class="pl-en">onClick</span><span class="pl-c1">=</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-en">setFoo</span><span class="pl-kos">(</span><span class="pl-s">"bar"</span><span class="pl-kos">)</span><span class="pl-kos">}</span><span class="pl-c1">&gt;</span><span class="pl-kos">{</span>foo<span class="pl-kos">}</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span>div&gt;<span class="pl-kos">)</span>;
    <span class="pl-kos">}</span>

    <span class="pl-k">const</span> <span class="pl-kos">{</span> <span class="pl-c1">getByTestId</span> <span class="pl-kos">}</span> <span class="pl-c1">=</span> <span class="pl-en">render</span><span class="pl-kos">(</span><span class="pl-kos">&lt;</span><span class="pl-smi">MyComponent</span><span class="pl-kos"></span> <span class="pl-c1">/</span>&gt;<span class="pl-kos">)</span>
    <span class="pl-s1">const</span> <span class="pl-s1">testElement</span> <span class="pl-c1">=</span> <span class="pl-en">getByTestId</span><span class="pl-kos">(</span><span class="pl-s">"test"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">testElement</span><span class="pl-kos">.</span><span class="pl-c1">textContent</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toEqual</span><span class="pl-kos">(</span><span class="pl-s">"bir"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">callback</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toBeCalledTimes</span><span class="pl-kos">(</span><span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-en">act</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-s1">fireEvent</span><span class="pl-kos">.</span><span class="pl-en">click</span><span class="pl-kos">(</span><span class="pl-s1">testElement</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-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">testElement</span><span class="pl-kos">.</span><span class="pl-c1">textContent</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toEqual</span><span class="pl-kos">(</span><span class="pl-s">"bar"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">callback</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toBeCalledTimes</span><span class="pl-kos">(</span><span class="pl-c1">2</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-en">act</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-s1">fireEvent</span><span class="pl-kos">.</span><span class="pl-en">click</span><span class="pl-kos">(</span><span class="pl-s1">testElement</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-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">testElement</span><span class="pl-kos">.</span><span class="pl-c1">textContent</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toEqual</span><span class="pl-kos">(</span><span class="pl-s">"bar"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">callback</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toBeCalledTimes</span><span class="pl-kos">(</span><span class="pl-c1">2</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// gets 3 here</span>

    <span class="pl-c">/*</span>
<span class="pl-c">     assuming we update the last line as follows</span>
<span class="pl-c">     expect(callback).toBeCalledTimes(3); // really should be 2</span>
<span class="pl-c">     act(() =&gt; { fireEvent.click(testElement); });</span>
<span class="pl-c">     expect(testElement.textContent).toEqual("bar");</span>
<span class="pl-c">     expect(callback).toBeCalledTimes(3); // does not re-render</span>
<span class="pl-c">    */</span>

  <span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>

<p dir="auto">Link to code example:</p>

<ul dir="auto">
<li>Similar code in <a href="https://codesandbox.io/s/rerender-on-first-two-clicks-700c0?file=/src/App.js" rel="nofollow">codesandbox</a></li>
<li>Simlar AND working code in <a href="https://snack.expo.dev/@trajano/usestate-test" rel="nofollow">an Expo Snack</a></li>
</ul>
<h2 dir="auto">The current behavior</h2>
<p dir="auto">On initial render OK<br>
On first click, rerender due to value change OK<br>
On second click, rerender even with the same value NOT OK</p>
<h2 dir="auto">The expected behavior</h2>
<p dir="auto">On initial render OK<br>
On first click, rerender due to value change OK<br>
On second click, no rerender because useState dispatch with the same value</p>
<p dir="auto">Note this works correctly on React-Native as shown by Expo</p>
<p dir="auto">React version:</p>
<h2 dir="auto">Steps To Reproduce</h2>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="import { useState, useEffect, useRef } from &quot;react&quot;;
function App() {
  const [count, setCount] = useState(0);
  const countLast = useRef(0);

  function handleClick() {
    const num = 1;
    setCount(num);
    console.log(num == countLast.current, &quot;set num&quot;, num, &quot;last count:&quot;, countLast.current);
  }
  useEffect(() =&gt; {
    countLast.current = count;
    console.log(&quot;effect&quot;);
  });
  console.log(&quot;Render&quot;);
  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;Next&lt;/button&gt;
      {count}
    &lt;/div&gt;
  );
}

export default App;"><pre class="notranslate"><code class="notranslate">import { useState, useEffect, useRef } from "react";
function App() {
  const [count, setCount] = useState(0);
  const countLast = useRef(0);

  function handleClick() {
    const num = 1;
    setCount(num);
    console.log(num == countLast.current, "set num", num, "last count:", countLast.current);
  }
  useEffect(() =&gt; {
    countLast.current = count;
    console.log("effect");
  });
  console.log("Render");
  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;Next&lt;/button&gt;
      {count}
    &lt;/div&gt;
  );
}

export default App;
</code></pre></div>
<p dir="auto">Link to code example:<br>
<a href="https://codesandbox.io/s/confident-satoshi-3othy?file=/src/App.js" rel="nofollow">https://codesandbox.io/s/confident-satoshi-3othy?file=/src/App.js</a></p>

<h2 dir="auto">The current behavior</h2>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/22582226/147873129-89338b13-0e93-485e-a3b2-2cb9dedd2b64.png"><img src="https://user-images.githubusercontent.com/22582226/147873129-89338b13-0e93-485e-a3b2-2cb9dedd2b64.png" alt="image" style="max-width: 100%;"></a></p>
<h2 dir="auto">The expected behavior</h2>
<p dir="auto">When I clicked the button for the second time, The "Render" string is printed, But the third and after clicks are not printed.<br>
The second click should be consistent with the follow-up, it is not printing "Render" strings, beacuse <code class="notranslate">num == countLast.current</code> is True.<br>
This inconsistency of behavior can cause a lot of confusion.</p>