Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
diff --git a/node_modules/@shopify/flash-list/dist/recyclerview/ViewHolderCollection.js b/node_modules/@shopify/flash-list/dist/recyclerview/ViewHolderCollection.js
index 8e3db51..85738f0 100644
--- a/node_modules/@shopify/flash-list/dist/recyclerview/ViewHolderCollection.js
+++ b/node_modules/@shopify/flash-list/dist/recyclerview/ViewHolderCollection.js
@@ -7,6 +7,7 @@ import React, { useEffect, useImperativeHandle, useLayoutEffect } from "react";
import { ViewHolder } from "./ViewHolder";
import { CompatView } from "./components/CompatView";
import { useRecyclerViewContext } from "./RecyclerViewContextProvider";
+import { Platform } from "react-native";
/**
* ViewHolderCollection component that manages the rendering of multiple ViewHolder instances
* and handles layout updates for the entire collection
@@ -72,9 +73,13 @@ export const ViewHolderCollection = (props) => {
// return `${index} => ${reactKey}`;
// })
// );
+ const renderEntries = Array.from(renderStack.entries());
+ if (Platform.OS === "web") {
+ renderEntries.sort(([, a], [, b]) => a.index - b.index);
Comment on lines +18 to +19

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle inverted lists when forcing natural DOM order

This sort always uses ascending index, but FlashList still supports inverted on web (RecyclerView/ViewHolder reverse the visual order via the inverted transform helpers). In that mode the DOM will now be deterministically ordered opposite to what users see, so keyboard and screen-reader traversal stays backwards for any inverted FlashList even though this patch is meant to fix accessibility ordering. The web sort needs to account for inverted (and horizontal inverted) instead of always using a.index - b.index.

Useful? React with 👍 / 👎.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have yet any components that are using inverted prop with FlashList. I did test it on FlashList 2.3.0 without the patch and let's just say inverted does a lot of things at once and navigation there looks weird in general. For example, list starts from the bottom and tabbing goes up, not down (without the patch). The patch does not change that behavior. Maybe it's warranted to come back to it when we have some components that do use that prop and reevaluate, but the behavior looks good to me at this point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VickyStash Can you please look into this when you use this prop?

Copy link
Contributor

@VickyStash VickyStash Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the PR #85114 where I'm working on migration main chat to FlashList with the inverted flag (P.S. I have three patches there as well). So what should I test specifically?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I've also tested this PR over the scenario when main chat (so expense details too) are migrated to FlashList, and I see some wrong ordering when I use voice over:

Monosnap.screencast.2026-03-20.09-32-05.mp4

But since FlashList is not used for the main chat in the current prod, I guess it can be solved later.

Just one question from me, does this PR then fixes the original issue for production? Since at this moment FlatList is used for main chat/expense details on prod?

+ }
return (React.createElement(CompatView, { style: hasData && containerStyle }, containerLayout &&
hasData &&
- Array.from(renderStack.entries(), ([reactKey, { index }]) => {
+ renderEntries.map(([reactKey, { index }]) => {
const item = data[index];
// Suppress separators for items in the last row to prevent
// height mismatch. The last data item has no separator (no
8 changes: 8 additions & 0 deletions patches/@shopify/flash-list/details.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@
- Upstream PR/issue: TBD
- E/App issue: https://github.com/Expensify/App/issues/33725
- PR introducing patch: https://github.com/Expensify/App/pull/81566


### [@shopify+flash-list+2.3.0+003+sort-for-natural-DOM-order.patch](@shopify+flash-list+2.3.0+003+sort-for-natural-DOM-order.patch)

- Reason: Fixes random DOM order in virtualized list items. Forces natural DOM order in the list.
- Upstream PR/issue: TBD
- E/App issue: https://github.com/Expensify/App/pull/80122
- PR introducing patch: https://github.com/Expensify/App/pull/85825
Loading