facebook/react

Concurrent mode renders components twice with different identities

Closed this issue · 6 comments

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

If concurrent mode is enabled, React renders functional components twice and (what's worse) with different identities under certain circumstances. The source code below always produces the bug.

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.

let instanceId = 0;

const MyFunctionalComponent = (props: any) => {
    console.log('Instance ' + useRef(instanceId++).current);
    return <h1>Hello world.</h1>;
};

const rootElement = document.getElementById('root');

(ReactDOM as any).createRoot(rootElement).render(<MyFunctionalComponent />);

// Doesn't produce the bug:
// ReactDOM.render(<MyFunctionalComponent />, rootElement);

Output:

Instance 0
Instance 1

What is the expected behavior?

One or more lines "Instance 0" in the console log. (Better: One single line "Instance 0".)

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

0.0.0-experimental-f42431abe

Additional notes

The bug also happens deep in the component tree and can cause costly operations to be performed twice. (In my case, those operations are done asynchronously in a class instance memorized via useRef.)

This is not a bug. And you'll have the same behavior in Strict Mode too. We intentionally double-call render-phase lifecycles in development only (and function components using Hooks) to help people find issues caused by side effects in render. In our experience, them firing twice is enough for people to notice and fix such bugs.

If component output is always a function of props and state (and not outer scope variables, like in your example), the double rendering in development should have no observable effect.

Thank you! This makes sense.

very cool

Why did console.log be disabled when the second call of the function Component??
const App=()=>{ debbugger; console.log('App'); return null }
my App stop on the debugger twice,But in the second time,console.log won‘t print anything in the Chrome Browser Console。

Mostly because the downsides of double rendering console logs, in our experience, outweigh the downsides of missing logs from one of them. We plan to make this configurable via DevTools. In the meantime you can do let log = console.log in outer scope and it’ll work on both renders.

Mostly because the downsides of double rendering console logs, in our experience, outweigh the downsides of missing logs from one of them. We plan to make this configurable via DevTools. In the meantime you can do let log = console.log in outer scope and it’ll work on both renders.

Learned