From 4ab7d531fb46a8189c3fcba1801317e71a17bdce Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Tue, 22 Apr 2025 10:32:55 -0700 Subject: [PATCH] fix a crash in view culling when the differentiator produces create instruction for existing tag (#50843) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/50843 changelog: [internal] Fix a crash where view culling produces a mutation to create a view that was already created. Without the change in Differentiator.cpp, the test fails assert on [StubViewTree.cpp:69](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactCommon/react/renderer/mounting/stubs/StubViewTree.cpp#L69). Reviewed By: rubennorte Differential Revision: D72818343 fbshipit-source-id: 8aec3ccf967f453c619a9495dcd32b43b21afea3 --- .../__tests__/ScrollView-viewCulling-itest.js | 77 +++++++++++++++++++ .../renderer/mounting/Differentiator.cpp | 6 +- 2 files changed, 79 insertions(+), 4 deletions(-) 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 6384be0e4b4..4e34d8cd569 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 @@ -1162,3 +1162,80 @@ test('scroll view parent is unflattened and view becomes culled', () => { 'Insert {type: "ScrollView", parentNativeID: "unflattened", index: 0, nativeID: (N/A)}', ]); }); + +test('parent-child flattening with culling', () => { + const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100}); + + Fantom.runTask(() => { + root.render( + + + + + + + , + ); + }); + + 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: (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: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Insert {type: "ScrollView", parentNativeID: (root), index: 0, nativeID: (N/A)}', + ]); + + // force parent-child to be flattened. + Fantom.runTask(() => { + root.render( + + + + + + + , + ); + }); + + expect(root.takeMountingManagerLogs()).toEqual([ + 'Update {type: "View", nativeID: "child"}', + 'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "child"}', + 'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}', + 'Delete {type: "View", nativeID: (N/A)}', + 'Delete {type: "View", nativeID: (N/A)}', + 'Insert {type: "View", parentNativeID: (N/A), index: 0, 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 03b3f796504..adbae7421f7 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -663,8 +663,6 @@ static void calculateShadowViewMutationsFlattener( // Case 1: child mode is the same as parent. // This is a flatten-flatten, or unflatten-unflatten. if (childReparentMode == reparentMode) { - // TODO(T217775046): Find a test case for this branch of view - // flattening + culling. calculateShadowViewMutationsFlattener( scope, childReparentMode, @@ -679,8 +677,8 @@ static void calculateShadowViewMutationsFlattener( : parentTag), subVisitedNewMap, subVisitedOldMap, - adjustedOldCullingContext, - adjustedNewCullingContext); + oldCullingContext, + newCullingContext); } else { // Get flattened nodes from either new or old tree // TODO(T217775046): Find a test case for this branch of view