MetaMask / MetaMask/metamask-mobile

Memoize useAccountNames account-group map build and returned array

Open Beginner friendly
#31,372 1 comment 0 reactions 0 assignees View on GitHub
area-performance Sev3 size-S ta-triaged team-confirmations team-mobile-platform
Dominant language
TypeScript
Stars
3k
Forks
1.7k
Avg merge
1d 14h
Merged PRs (30d)
669

Description

> **Performance audit finding** · Severity: **Medium** · Effort: Easy · Fix risk: Simple · Test safety net: Covered (app/components/hooks/DisplayName/useAccountNames.test.ts)
> Owner: `@MetaMask/confirmations (suggested)`
> File: `app/components/hooks/DisplayName/useAccountNames.ts:11`

### What is this about?

`useAccountNames` rebuilds a full `accountGroupNames` lookup map on **every render** by iterating every account group and every account inside it, with no `useMemo`. It then returns a fresh `.map()` array (line 22) — also unmemoized.
```ts
const accountGroupNames = accountGroups.reduce((acc, group) => {
group.accounts.forEach((accountId) => {
const account = internalAccountsById[accountId];
acc[account.address.toLowerCase()] = group.metadata.name;
});
return acc;
}, {} as Record);

return requests.map((request) => {
const { value } = request;
return accountGroupNames[value.toLowerCase()];
});
```

**Why it matters**

This hook is one of the inputs aggregated by `useDisplayNames` (the wave-1 `unstablehook-usedisplaynames-fresh-array` finding fixes only the top-level array). `useDisplayName`/`useDisplayNames` runs once per name/address rendered — in confirmation screens, simulation rows, and transaction lists, frequently per-row in virtualized lists. For a wallet with many accounts/groups, the `reduce`-over-all-groups runs on every render of every row, and the returned array is a new reference each time, defeating downstream memoization in `useDisplayNames`.

### Scenario

N/A — see Technical Details.

### Design

N/A — internal performance change; no UI/design impact.

### Technical Details

**Evidence**

`app/components/hooks/DisplayName/useAccountNames.ts:11` (map rebuild) and `:22` (fresh return array). Both run unconditionally on every render because nothing is wrapped in `useMemo`.

**Fix**

Wrap the map build in `useMemo` keyed on `[accountGroups, internalAccountsById]`, and wrap the final `requests.map(...)` in `useMemo` keyed on `[requests, accountGroupNames]`:
```ts
const accountGroupNames = useMemo(() => accountGroups.reduce(...), [accountGroups, internalAccountsById]);
return useMemo(() => requests.map((r) => accountGroupNames[r.value.toLowerCase()]), [requests, accountGroupNames]);
```
(Pair with the wave-1 fix that stabilizes the `requests` array passed in from `useDisplayName`.)

### Threat Modeling Framework

N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.

### Acceptance Criteria

- - Run `yarn jest app/components/hooks/DisplayName/useAccountNames.test.ts`.
- Add a test rendering the hook twice with identical `requests` and unchanged Redux state; assert the returned array is `===` across renders.

### References

- File: `app/components/hooks/DisplayName/useAccountNames.ts:11`
- Source: MetaMask Mobile performance audit — finding `unstablehook-useaccountnames-rebuild-map-every-render`
- Owner (CODEOWNERS / best-effort): @MetaMask/confirmations (suggested)
- Status: **UNVALIDATED**

Contributor guide

Open the contributing guide

Research direction

Start with app/components/hooks/DisplayName/useAccountNames.ts, then run app/components/hooks/DisplayName/useAccountNames.test.ts. Review the account-group lookup and returned request-name array, and add coverage for repeated renders with unchanged inputs; done means the existing test suite passes and the returned array remains referentially stable.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend, mobile
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.