Skip to content

Commit 9253fc3

Browse files
rozelefacebook-github-bot
authored andcommitted
Defer focus cell render mask updates (facebook#52380)
Summary: Pull Request resolved: facebook#52380 Apps that rely support focus in FlatList rendered items are missing out on a FlatList optimization that defers rendering for offscreen content updates. For example, on Android, if you focus and smooth scroll an item into view, the onScroll event will fire first. For most sufficiently large virtualization windows, the next render will be delayed by the render batch timeout as most materialization of virtualized views is not treated as a high pri render. However, this batch / timeout mechanism isn't being used for cell render updates that occur as a result of a focus change. This change adds the same timeout mechanism used for scroll events. In most cases, the view that is focused is in the viewport, and the extra rendering needed is already scheduled (or executed with high priority if needed) when the onScroll event is processed. In cases where the focus change occurs outside the viewport, most platforms will want to do some kind of "bring into view" anyway, and the same applies - onScroll will take care of scheduling the cell rendering priority. ## Changelog [Internal] Reviewed By: NickGerleman Differential Revision: D77681274 fbshipit-source-id: 1ade377e513eca21338a380ff9299dd410606aec
1 parent 45fd7fe commit 9253fc3

File tree

3 files changed

+25
-2
lines changed

3 files changed

+25
-2
lines changed

packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,17 @@ const definitions: FeatureFlagDefinitions = {
706706
},
707707
ossReleaseStage: 'none',
708708
},
709+
deferFlatListFocusChangeRenderUpdate: {
710+
defaultValue: false,
711+
metadata: {
712+
dateAdded: '2025-07-02',
713+
description:
714+
'Use the deferred cell render update mechanism for focus change in FlatList.',
715+
expectedReleaseValue: true,
716+
purpose: 'experimentation',
717+
},
718+
ossReleaseStage: 'none',
719+
},
709720
disableInteractionManager: {
710721
defaultValue: true,
711722
metadata: {

packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<670f6eab8583db989988cbf1a28383a2>>
7+
* @generated SignedSource<<b75fccb46a36b07c692d890f0659f9a3>>
88
* @flow strict
99
* @noformat
1010
*/
@@ -33,6 +33,7 @@ export type ReactNativeFeatureFlagsJsOnly = $ReadOnly<{
3333
animatedShouldDebounceQueueFlush: Getter<boolean>,
3434
animatedShouldUseSingleOp: Getter<boolean>,
3535
avoidStateUpdateInAnimatedPropsMemo: Getter<boolean>,
36+
deferFlatListFocusChangeRenderUpdate: Getter<boolean>,
3637
disableInteractionManager: Getter<boolean>,
3738
enableAccessToHostTreeInFabric: Getter<boolean>,
3839
fixVirtualizeListCollapseWindowSize: Getter<boolean>,
@@ -135,6 +136,11 @@ export const animatedShouldUseSingleOp: Getter<boolean> = createJavaScriptFlagGe
135136
*/
136137
export const avoidStateUpdateInAnimatedPropsMemo: Getter<boolean> = createJavaScriptFlagGetter('avoidStateUpdateInAnimatedPropsMemo', true);
137138

139+
/**
140+
* Use the deferred cell render update mechanism for focus change in FlatList.
141+
*/
142+
export const deferFlatListFocusChangeRenderUpdate: Getter<boolean> = createJavaScriptFlagGetter('deferFlatListFocusChangeRenderUpdate', false);
143+
138144
/**
139145
* Disables InteractionManager and replaces its scheduler with `setImmediate`.
140146
*/

packages/virtualized-lists/Lists/VirtualizedList.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import {
6464
View,
6565
findNodeHandle,
6666
} from 'react-native';
67+
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
6768

6869
export type {ListRenderItemInfo, ListRenderItem, Separators};
6970

@@ -1332,7 +1333,12 @@ class VirtualizedList extends StateSafePureComponent<
13321333

13331334
_onCellFocusCapture = (cellKey: string) => {
13341335
this._lastFocusedCellKey = cellKey;
1335-
this._updateCellsToRender();
1336+
if (ReactNativeFeatureFlags.deferFlatListFocusChangeRenderUpdate()) {
1337+
// Schedule the cells to render update the same way we handle scroll or layout events.
1338+
this._scheduleCellsToRenderUpdate();
1339+
} else {
1340+
this._updateCellsToRender();
1341+
}
13361342
};
13371343

13381344
_onCellUnmount = (cellKey: string) => {

0 commit comments

Comments
 (0)