From 954e294183d9681b66aba6785f7e3606ef180787 Mon Sep 17 00:00:00 2001 From: Andrei Marchenko Date: Wed, 21 Aug 2024 14:23:07 -0700 Subject: [PATCH] perf(virtualized-lists) removing rerender cells for every change of Virtualized list (#46103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46103 Reducing the boundary of rerender of virtual lists. Previously with prop: "strictMode={true}" the VirtualizedList still re rendered each CellRenderer component. Because method getDerivedStateFromProps generated every time a new uniq state and the cells didn’t have a PureComponent. It helps to improve react performance for lists which have 5+ elements. I reused recomended approach from react doc https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#what-about-memoization changelog: [internal] Optimizing CellRenderer of VirtualizedList Reviewed By: NickGerleman, sammy-SC Differential Revision: D61493434 fbshipit-source-id: 917a33e48bd2f18e8ac150e5701d2e7c45dbe879 --- .../ReactNativeFeatureFlags.config.js | 5 ++++ .../featureflags/ReactNativeFeatureFlags.js | 8 +++++- .../Lists/VirtualizedListCellRenderer.js | 27 ++++++++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index eb0b6cb32e0..011db86349e 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -270,6 +270,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Enables access to the host tree in Fabric using DOM-compatible APIs.', }, + enableOptimisedVirtualizedCells: { + defaultValue: false, + description: + 'Removing unnecessary rerenders Virtualized cells after any rerenders of Virualized list. Works with strict=true option', + }, isLayoutAnimationEnabled: { defaultValue: true, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 06297165f30..72a708f1e8a 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<4ec8afb16cae6c04f166b0c28a906aff>> * @flow strict-local */ @@ -30,6 +30,7 @@ export type ReactNativeFeatureFlagsJsOnly = { animatedShouldDebounceQueueFlush: Getter, animatedShouldUseSingleOp: Getter, enableAccessToHostTreeInFabric: Getter, + enableOptimisedVirtualizedCells: Getter, isLayoutAnimationEnabled: Getter, shouldSkipStateUpdatesForLoopingAnimations: Getter, shouldUseAnimatedObjectForTransform: Getter, @@ -111,6 +112,11 @@ export const animatedShouldUseSingleOp: Getter = createJavaScriptFlagGe */ export const enableAccessToHostTreeInFabric: Getter = createJavaScriptFlagGetter('enableAccessToHostTreeInFabric', false); +/** + * Removing unnecessary rerenders Virtualized cells after any rerenders of Virualized list. Works with strict=true option + */ +export const enableOptimisedVirtualizedCells: Getter = createJavaScriptFlagGetter('enableOptimisedVirtualizedCells', false); + /** * Function used to enable / disabled Layout Animations in React Native. */ diff --git a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js index 6916c49c00d..cc05755b13e 100644 --- a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js +++ b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js @@ -8,6 +8,7 @@ * @format */ +import * as ReactNativeFeatureFlags from '../../react-native/src/private/featureflags/ReactNativeFeatureFlags'; import type {CellRendererProps, RenderItemType} from './VirtualizedListProps'; import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; import type { @@ -53,7 +54,7 @@ type State = { ... }; -export default class CellRenderer extends React.Component< +export default class CellRenderer extends React.PureComponent< Props, State, > { @@ -68,12 +69,24 @@ export default class CellRenderer extends React.Component< props: Props, prevState: State, ): ?State { - return { - separatorProps: { - ...prevState.separatorProps, - leadingItem: props.item, - }, - }; + if (ReactNativeFeatureFlags.enableOptimisedVirtualizedCells()) { + if (props.item !== prevState.separatorProps.leadingItem) { + return { + separatorProps: { + ...prevState.separatorProps, + leadingItem: props.item, + }, + }; + } + return null; + } else { + return { + separatorProps: { + ...prevState.separatorProps, + leadingItem: props.item, + }, + }; + } } // TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not