From bc7b8807f6ecd23b1a6794fdbee969fa5e77706c Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 14 May 2025 04:22:00 -0700 Subject: [PATCH] add test for ViewCulling when sibling in FLatList resizes (#51311) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51311 changelog: [internal] add a test case for view culling to cover a crash observed in production. Reviewed By: lenaic, rubennorte Differential Revision: D74720814 --- .../__tests__/ScrollView-viewCulling-itest.js | 53 ++++++++++++++++++- .../renderer/mounting/Differentiator.cpp | 1 - 2 files changed, 52 insertions(+), 2 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 95a414fd9ef7..3b39c83cad17 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 @@ -18,8 +18,9 @@ import type {HostInstance} from 'react-native'; import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance'; import * as Fantom from '@react-native/fantom'; +import nullthrows from 'nullthrows'; import * as React from 'react'; -import {Modal, ScrollView, View} from 'react-native'; +import {FlatList, Modal, ScrollView, View} from 'react-native'; import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement'; test('basic culling', () => { @@ -818,6 +819,56 @@ test('culling inside of Modal', () => { ]); }); +test('nesting inside FlatList with item resizing', () => { + const root = Fantom.createRoot({viewportHeight: 100, viewportWidth: 100}); + let _setIsExpanded = null; + function ExpandableComponent() { + const [isExpanded, setIsExpanded] = React.useState(false); + _setIsExpanded = setIsExpanded; + return {isExpanded && }; + } + + Fantom.runTask(() => { + root.render( + { + if (item.key === 'one') { + return ; + } else if (item.key === 'two') { + return ( + // position: 'absolute' is the important part that prevents Yoga from overcloning. + // When Yoga overclones, differentiator visits all cloned nodes and culling is correctly + // applied. + + + + + + ); + } + }} + />, + ); + }); + + expect(root.takeMountingManagerLogs()).toContain( + 'Create {type: "View", nativeID: "child"}', + ); + + Fantom.runTask(() => { + nullthrows(_setIsExpanded)(true); + }); + + expect(root.takeMountingManagerLogs()).toContain( + 'Delete {type: "View", nativeID: "child"}', + ); +}); + describe('reparenting', () => { test('view flattening with culling', () => { const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100}); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index 6dc39b70e749..acd3c7073c3d 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -280,7 +280,6 @@ static void updateMatchedPairSubtrees( return; } - // TODO(T217775046): find a test case for this branch. auto oldCullingContextCopy = oldCullingContext.adjustCullingContextIfNeeded(oldPair); auto newCullingContextCopy =