Skip to content

Performance: feat - token options memoization - #2317

Open
JohnathanWhite wants to merge 4 commits into
bitpay:developfrom
JohnathanWhite:perf/token-options-memoization
Open

Performance: feat - token options memoization#2317
JohnathanWhite wants to merge 4 commits into
bitpay:developfrom
JohnathanWhite:perf/token-options-memoization

Conversation

@JohnathanWhite

Copy link
Copy Markdown
Collaborator

Issue

Seven screens each built the merged token map inline inside useAppSelector:

useAppSelector(({WALLET}) => ({
  ...BitpaySupportedTokenOptsByAddress,
  ...tokenOptionsByAddress,
  ...WALLET.customTokenOptionsByAddress,
}))

useSelector runs on every store dispatch, so this spread a multi-thousand-key object on every dispatch, for every mounted instance. And because the result was always a fresh reference, the equality check could never bail — so the component
re-rendered on every dispatch too. Worst on the 1,500–1,900 line KeyOverview/AccountDetails screens and on per-row components (ContactIcon, MultipleOutputsTx).

A shared useTokenOptionsByAddress hook now selects only the custom-token map (a stable reference) and merges in useMemo, so the identity stays stable until the inputs actually change.

Also fixes a bug: ContactIcon spread the const it was declaring instead of the context value, so the 1inch token map was silently dropped there. eslint had been flagging the unused binding.

Second commit applies the hook to GlobalSelect and cleans up its render path: it walked the entire token registry and ran ~8 sequential wallet filter passes bare in the render body, so all of it re-ran on every search keystroke and every
modal toggle. That's now memoized, it selects one WALLET field instead of the whole slice, and its two FlashLists key on stable ids instead of array indices (index keys over filtered, changing data break recycling and can reuse a row for the
wrong wallet).

How to test

  1. Token icons and names resolve correctly on KeyOverview, AccountDetails, KeySettings, Add Custom Token, transaction detail with multiple outputs, and the contacts list. Some icons may now appear where a generic fallback showed before —
    that's the ContactIcon fix, not a regression.
  2. Open the wallet/coin picker: type quickly in search, toggle chain filters, scroll. Rows must show the correct wallet — a wrong-row or duplicated row would indicate a list-key problem.
  3. Add a custom token and confirm it still appears everywhere it should.
  4. Send and swap flows still resolve token metadata correctly.

Six screens each built the merged token map inline inside useAppSelector:

  useAppSelector(({WALLET}) => ({
    ...BitpaySupportedTokenOptsByAddress,
    ...tokenOptionsByAddress,
    ...WALLET.customTokenOptionsByAddress,
  }))

useSelector runs on every store dispatch, so that spread a multi-thousand-key
object on every dispatch for every mounted instance - and because the result was
always a fresh reference the equality check could never bail, so the component
re-rendered on every dispatch too. Worst on 1,500-1,900 line screens and on
per-row components like ContactIcon and MultipleOutputsTx.

The new hook selects only the custom-token map (a stable reference) and merges in
useMemo, so the identity is stable until the inputs actually change.

Also fixes a bug in ContactIcon: it spread the const it was declaring instead of
the context value, so the 1inch token map was silently dropped there. Token icons
may now resolve where they previously fell back to a generic icon.
GlobalSelect is the app's most-used picker. It walked the entire token registry
and ran about eight sequential wallet filter passes bare in the render body, so
all of it re-ran on every search keystroke and every modal toggle. That work is
now memoized, and the screen selects the one WALLET field it needs instead of the
whole slice.

Its two FlashLists also used array indices as keys over filtered, changing data,
which breaks recycling and can reuse a row for the wrong wallet. They now key on
stable ids.
@gabrielbazan7

Copy link
Copy Markdown
Collaborator

Follow-up to 1903baa and 40ee652: JohnathanWhite#6

…balselect-deps

Performance: ref - derive list ids from data and fix GlobalSelect mem…
@JohnathanWhite
JohnathanWhite marked this pull request as ready for review August 25, 2026 14:39
@gabrielbazan7

gabrielbazan7 commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Already implemented here: #2241. We can close this one.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants