perf: Migrate to Flashlist in MoneyRequestReportTransactionList#91422
perf: Migrate to Flashlist in MoneyRequestReportTransactionList#91422TMisiukiewicz wants to merge 104 commits into
Conversation
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
@aimane-chnaif Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b5c9bef51
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
fixed in 970d609
fixed in 335748a
fixed in 335748a
Nothing in the codebase ever writes a transactionID into that param. I don't think we should write code for a flow that does not exist
not valid
I don't think we have any bug introduced by lack of extraData so I am not in favor of adding this unless necessary
React Compiler handles this
not valid I'll check the resize and send message bugs soon |
|
@aimane-chnaif I can't reproduce the resize bug - can you verify again? Might be accidentally fixed by one of my recent commits 😅 |
|
🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here. |
|
also couldn't reproduce the issue with the scroll after msg |
This comment has been minimized.
This comment has been minimized.
|
@aimane-chnaif narrow and wide are two structurally different lists (main scroll list vs separate horizontally-scrollable table). Swapping between them means one unmounts and the other mounts fresh, causing a single frame blank while the new list measures itself. It only happens right at breakpoint and normal usage almost never hit it - I'd say it's normal React behavior, wdyt? Regarding blank spaces in list items on scroll, I was able to reproduce it for a very short time, but it's not something clearly reproducible, here's the vid: Screen.Recording.2026-07-14.at.06.34.52.movAdditionally, I was able to reproduce it with scrolling search page too, so I think it might be general thing when scrolling really fast with heavy list items in FlashList. Possibly a thing for a separate issue. WDYT about moving forward with the PR as-is? |
Agree. The root cause exists in production so not blocker for this PR. Definitely out of scope. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2cb8c0f25
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
🤖 Review — I read the FlashList migration, the unified-list mechanics, selection/scroll behavior, the two Overall this looks solid. The core migration is well done: One thing worth a look before merge, plus some minor cleanups: Worth addressing
Minor / cleanupFive low-severity items
Checked and clean
None of the above is a hard blocker; the |
|
Please check AI review comments |
|
🤖 Regression hunt — I compared each changed file at head ( Verdict: no confirmed data-loss/crash regressions. Two confirmed behavior changes worth fixing, one high-blast-radius patch to QA, and a few suspected items needing a runtime check. Core transaction behaviors (sort, group, totals, selection, long-press, RBR) are preserved — details at the bottom. Confirmed — worth addressing1. Pagination thresholds silently dropped (0.75 → FlashList's ~0.5 default). [MEDIUM] The unified list wires 2. High blast radius — needs manual QA3. Patch Suspected — verify at runtimeFour items to confirm on device/browser
Correction to my earlier reviewThe " Verified preserved — NOT regressions
The two cheap wins are re-adding the pagination thresholds (#1) and the per- |
|
🤖 Regression hunt — I compared the PR head against Bottom line: the core money-request behaviors are preserved (selection operates on full data, totals footer, sort/group, unread-marker index tracking is correctly compensated, and the RBR change is a deliberate fix). The regression risk is concentrated on the chat / report-actions side and the patched flash-list scroll math. Five concrete candidates, none confirmed a hard blocker but the first three are worth verifying at runtime. Regression candidates1. [medium] 2. [medium] 3. [medium] Horizontal table scroll position no longer restored across data changes. 4. [medium] Deep-link scroll can miss on a large report. 5. [low] Pagination thresholds changed. Old FlatList set Patch risk (load-bearing)The Checked and confirmed NOT regressed
Top three to smoke-test: (1) cold-open a report with older chat history — watch for an early fetch/jump; (2) deep-link into a chat message on a report with many slow-loading transactions — confirm it lands on the message; (3) horizontal-scroll a wide table, then add/re-sort — confirm position holds. Reply with |
5c17a83 to
a71286a
Compare
|
@aimane-chnaif addressed all valid comments 🙏 |
…rt-list # Conflicts: # src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx
|
@aimane-chnaif resolved conflicts, ready for final review |
|
🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|


Explanation of Change
Problem
On heavy money-request reports the RHP list was effectively non-virtualised. The inner transactions section was rendered via plain
.map()inside the parent FlatList'sListHeaderComponent, which renders eagerly. Every transaction row mounted synchronously when the report opened, pushing report open time to ~20–30s for a report with 1600 expenses and producing a noticeable jank window on every navigation.Solution
Unified list — merged the inner transactions section and the outer report-actions section into a single virtualised list. A discriminated-union
UnifiedListItem(section-header|transaction|transactions-footer|report-action) flows through onerenderItemdispatcher, so transactions and chat messages now share the same recycler window instead of one being eagerly rendered inside the other's header.FlashList — migrated the list from
react-native'sFlatList(FlatListWithScrollKey).initialScrollIndexreplaces the FlatList-specificuseFlatListScrollKeywrapperController + sub-component —
MoneyRequestReportTransactionListcontinues to own the transaction-domain state (sort, group-by, selection, columns, totals, etc.) and exposes it to the parent via a render-prop controller. The parent renders a smallMoneyRequestReportUnifiedListsub-component that assembles the FlashList from controller data plus the report-actions listFixed Issues
$ #91425
PROPOSAL:
Tests
Before testing:
npm run i-standaloneas this PR contains patch to the FlashListOffline tests
N/A
QA Steps
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
ios.mov
iOS: mWeb Safari
MacOS: Chrome / Safari
web.mov