diff --git a/packages/react-reconciler/src/ReactChildFiber.js b/packages/react-reconciler/src/ReactChildFiber.js index 742206c47fbf5..e7534ac9e4347 100644 --- a/packages/react-reconciler/src/ReactChildFiber.js +++ b/packages/react-reconciler/src/ReactChildFiber.js @@ -1460,7 +1460,9 @@ function createChildReconciler( lanes: Lanes, debugInfo: ReactDebugInfo | null, ): Fiber | null { - // This function is not recursive. + // This function is only recursive for Usables/Lazy and not nested arrays. + // That's so that using a Lazy wrapper is unobservable to the Fragment + // convention. // If the top level item is an array, we treat it as a set of children, // not as a fragment. Nested arrays on the other hand will be treated as // fragment nodes. Recursion happens at the normal flow. @@ -1468,7 +1470,8 @@ function createChildReconciler( // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. - // TODO: Let's use recursion like we do for Usable nodes? + // We don't use recursion here because a fragment inside a fragment + // is no longer considered "top level" for these purposes. const isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && diff --git a/packages/react-reconciler/src/__tests__/ReactFragment-test.js b/packages/react-reconciler/src/__tests__/ReactFragment-test.js index 3db7702f4cfef..fe1d1f4f953db 100644 --- a/packages/react-reconciler/src/__tests__/ReactFragment-test.js +++ b/packages/react-reconciler/src/__tests__/ReactFragment-test.js @@ -965,4 +965,51 @@ describe('ReactFragment', () => { , ); }); + + it('should preserve state of children when adding a fragment wrapped in Lazy', async function () { + const ops = []; + + class Stateful extends React.Component { + componentDidUpdate() { + ops.push('Update Stateful'); + } + + render() { + return
Hello
; + } + } + + const lazyChild = React.lazy(async () => ({ + default: ( + <> + +
World
+ + ), + })); + + function Foo({condition}) { + return condition ? : lazyChild; + } + + ReactNoop.render(); + await waitForAll([]); + + ReactNoop.render(); + await waitForAll([]); + + expect(ops).toEqual(['Update Stateful']); + expect(ReactNoop).toMatchRenderedOutput( + <> +
Hello
+
World
+ , + ); + + ReactNoop.render(); + await waitForAll([]); + + expect(ops).toEqual(['Update Stateful', 'Update Stateful']); + expect(ReactNoop).toMatchRenderedOutput(
Hello
); + }); });