Performance: feat - token options memoization - #2317
Open
JohnathanWhite wants to merge 4 commits into
Open
Conversation
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.
Collaborator
|
Follow-up to 1903baa and 40ee652: JohnathanWhite#6 |
…balselect-deps Performance: ref - derive list ids from data and fix GlobalSelect mem…
JohnathanWhite
marked this pull request as ready for review
August 25, 2026 14:39
Collaborator
|
Already implemented here: #2241. We can close this one. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue
Seven screens each built the merged token map inline inside
useAppSelector:useSelectorruns 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 componentre-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
useTokenOptionsByAddresshook now selects only the custom-token map (a stable reference) and merges inuseMemo, so the identity stays stable until the inputs actually change.Also fixes a bug:
ContactIconspread 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
GlobalSelectand 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 everymodal 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
that's the ContactIcon fix, not a regression.