Skip to content

[Due for payment 2026-10-16] [$175] Perf: stop Search page header from rescanning todos on every update #103155

Description

@mountiny

Problem

SearchPageHeaderCommon called useSearchTypeMenuSections() only to look up the label of the current suggested search. That hook runs useHasReportAwaitingApproval, which rescans all reports and rebuilds a transactions-by-report index on every report or transaction write. Opening a report from the Spend tab triggers several such writes, so the header spent about 200ms per open on a scan that cannot change its title.

Solution

The header now reads suggestedSearches[currentSearchKey] from useSearchQueryContext(), which is already computed there. This removes the useSearchTypeMenuSections() call from the header.

PR

#103152

Issue OwnerCurrent Issue Owner: @getusha
Upwork Automation - Do Not Edit

Activity

  1. added
    ExternalAdded to denote the issue can be worked on by a contributor
    BugSomething is broken. Auto assigns a BugZero manager.
    on Oct 6, 2026
  2. melvin-bot commented on Oct 6, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @getusha (External)

  3. changed the title [-]Perf: stop Search page header from rescanning todos on every update[/-] [+][$175] Perf: stop Search page header from rescanning todos on every update[/+] on Oct 6, 2026
  4. melvin-bot commented on Oct 6, 2026

    @melvin-bot
  5. MelvinBot commented on Oct 6, 2026

    @MelvinBot
    Contributor

    Proposal

    The issue's diagnosis is correct. The open PR, Perf: stop Search page header from rescanning todos on every update, is a valid fix. It changes one small behavior, described below.

    What is the root cause of that problem?

    SearchPageHeaderCommon.tsx:18-20 calls useSearchTypeMenuSections() only to find the current suggested search's translationPath. That hook calls useHasReportAwaitingApproval. That hook subscribes to all reports, transactions, policies, and report metadata. On every render it runs getTodoReportsForSearchKey(APPROVE), which indexes all transactions and loops over every report. Each report or transaction write re-renders the header and repeats that scan. The scan cannot change the title.

    What changes do you think we should make in order to solve the problem?

    Read the item from suggestedSearches[currentSearchKey], which SearchQueryProvider already computes from the same getSuggestedSearches() without report or transaction subscriptions. This is what the PR does.

    • Same title for visible searches: getSearchPageHeaderTitle reads only translationPath. That value is constant per key, and the menu items are copies of the same objects. The types match (SearchTypeMenuItem).
    • Behavior change for hidden searches: The menu omits searches the user can't see, for example Approve, Pay, Export, or insights. If currentSearchKey points to one of them (a deeplink, a recent search, or Approve disappearing after the last approval), the old code fell back to "Spend". The new code shows the search's own label. I think this is better: it stops the title from changing when hasReportAwaitingApproval flips, and it matches how the chart title already works in Search/index.tsx:1413-1418. The reviewer should confirm it is acceptable.
    Remaining cost on the same screen

    This removes one of at least two scans on wide Search. SearchTypeMenuWide.tsx:83 still calls useSearchTypeMenuSectionsForNavigation(), which runs the same hook without a focus freeze. EmptySearchView and useNavigationSuggestions also mount it. A follow-up could let getTodoReportsForSearchKey stop at the first match, since this caller only checks .length > 0. A follow-up could also share one hasReportAwaitingApproval value across all these callers.

    What alternative solutions did you explore? (Optional)

    To keep the old "Spend" fallback for hidden searches, run getSuggestedSearchesVisibility on the result before using it. That adds back policy-based work for a case that is arguably wrong today, so I don't recommend it.


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/103155) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  6. added and removed
    ReviewingHas a PR in review
    on Oct 6, 2026
  7. changed the title [-][$175] Perf: stop Search page header from rescanning todos on every update[/-] [+][Due for payment 2026-10-16] [$175] Perf: stop Search page header from rescanning todos on every update[/+] on Oct 9, 2026
  8. melvin-bot commented on Oct 9, 2026

    @melvin-bot

    @getusha

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.6-6 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-10-16. 🎊

    The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the Contributor+ Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]. If no checklist is needed for this issue, you can click: [no checklist button]

  9. melvin-bot commented on Oct 9, 2026

    @melvin-bot

    @getusha Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorOverdue

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions