react icon indicating copy to clipboard operation
react copied to clipboard

Bug: Setting the state to the same value causes a re-render.

Open JoonDong2 opened this issue 1 year ago • 3 comments

React version: 18.3.1

Steps To Reproduce

  1. Set the initial state to false.
  2. Change the state to true.
  3. Set the state to true once more.

Link to code example: codesandbox

import React, { useState } from "react";

const Child = () => {
  console.log("Child");
  return null;
};

export default function App() {
  const [state, setState] = useState(false);
  console.log("App");
  return (
    <>
      <button
        onClick={() => {
          setState(true);
        }}
      >
        click
      </button>
      <Child />
    </>
  );
}

The current behavior

  1. Print 'App' and 'Child' on the first rendering.
  2. Clicking the prints 'App' and 'Child'.
  3. Clicking the once more prints 'App'.

The expected behavior

  1. Print 'App' and 'Child' on the first rendering.
  2. Clicking the prints 'App' and 'Child'.
  3. Clicking the one more time results in no reaction.

I think this issue occurred because the previous current(now alternate)'s lanes was not cleared after reconciliation.

I confirmed that when the button was clicked a second time, the lanes was not NoLanes, so it did not compare with the previous state and immediately scheduled the rendering. https://github.com/facebook/react/blob/v18.3.1/packages/react-reconciler/src/ReactFiberHooks.new.js#L2257-L2287

Since there were no updates during the rendering of the App component, it bailed out of rendering, so the children were not re-rendered. https://github.com/facebook/react/blob/v18.3.1/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L1047-L1050

However, if the lanes of the previous fiber had been cleared, such rendering would not have occurred.

I thought "If the state doesn't change, no re-rendering occurs", but I'm confused by the above result.

Is this a bug, or is there another reason for this behavior?

JoonDong2 avatar Aug 01 '24 02:08 JoonDong2

Hmm, for your info, this does not happen in the case of preact in the same situation (I tried.)

My Test Code with Preact

Console Input With Google Chrome index.tsx:15 App index.tsx:10 Child index.tsx:15 App index.tsx:10 Child

import { render, useState } from "preact/compat";

const Child = () => {
    console.log("Child");
    return <></>;
}

export default function App() {
    const [state, setState] = useState(false);
    console.log("App");

    return (
        <>
            <button onClick={() => setState(true)}>click {state ? "true" : "false"}</button>
            <Child />
        </>
    );
}

render(<App />, document.getElementById("render"));

So... I guess it's just a bug or not. 😂

MTtankkeo avatar Aug 02 '24 07:08 MTtankkeo

When you call setState(true), even though the state hasn't changed (it's still true), React schedules a re-render for the App component. During this process, React compares the new state with the previous state. Since they are the same, React recognizes that there's no need to re-render the child components. However, because the App component is already in the process of rendering, the code inside the App function, including console.log("App"), still runs. React then decides to skip re-rendering the Child component to optimize performance.

waizbart avatar Aug 19 '24 13:08 waizbart

I found a similar behavior and I don't understand if it's a bug.

Here is a simpler example codesandbox

Steps To Reproduce:

  1. Click on QQQ - the value changes, rendering happens
  2. Click on QQQ - the value does not change, rendering happens
  3. Each subsequent click on the QQQ button - the value does not change, rendering does not happen
  4. Next, we can repeat the same actions with another button

DarWiM avatar Sep 10 '24 08:09 DarWiM

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Dec 09 '24 09:12 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Dec 16 '24 10:12 github-actions[bot]