diff --git a/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js b/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js index a3291568068..bec55546a9e 100644 --- a/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js +++ b/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js @@ -1398,3 +1398,73 @@ test('unflattening and creating a subtree that is partially culled', () => { 'Insert {type: "View", parentNativeID: "child", index: 0, nativeID: "grandchild"}', ]); }); + +test('flattening and deleting a subtree that is partially culled', () => { + const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100}); + + // First render with a unflattened view container that is visible and a subtree that is partially culled. + Fantom.runTask(() => { + root.render( + + + + + + + , + ); + }); + + // All views are mounted, except for the grandchild. + expect(root.takeMountingManagerLogs()).toEqual([ + 'Update {type: "RootView", nativeID: (root)}', + 'Create {type: "ScrollView", nativeID: (N/A)}', + 'Create {type: "View", nativeID: (N/A)}', + 'Create {type: "View", nativeID: (N/A)}', + 'Create {type: "View", nativeID: "child"}', + 'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}', + 'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}', + ]); + + // Now change opacity to the default to flatten the container and delete container's subtree. + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + // Note that the grandchild is not deleted because it was not previously mounted. + expect(root.takeMountingManagerLogs()).toEqual([ + 'Update {type: "ScrollView", nativeID: (N/A)}', + 'Update {type: "View", nativeID: (N/A)}', + 'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}', + 'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Delete {type: "View", nativeID: (N/A)}', + 'Delete {type: "View", nativeID: "child"}', + ]); +}); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index 53c12b55af4..e6b86171c79 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -854,8 +854,6 @@ static void calculateShadowViewMutationsFlattener( if (!treeChildPair.flattened) { ViewNodePairScope innerScope{}; - // TODO(T217775046): Find a test case for this branch of view - // flattening + culling. calculateShadowViewMutations( innerScope, mutationContainer.destructiveDownwardMutations,