MetaMask / MetaMask/metamask-mobile

Wrap AccountNetworkIndicator in React.memo (rendered per account row)

Open Beginner friendly
#31,319 1 comment 0 reactions 0 assignees View on GitHub
area-performance Sev3 size-S ta-triaged team-mobile-ux
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: Partial
> Owner: `@MetaMask/mobile-core-ux (suggested)`
> File: `app/components/UI/AccountNetworkIndicator/AccountNetworkIndicator.tsx:14`

### What is this about?

`AccountNetworkIndicator` is exported as a plain function component (no `React.memo`).
It is rendered once per account row inside `CaipAccountSelectorList`'s
`renderAccountBalances`, and it runs its own `useSelector(getActiveNetworksByScopes)`
plus a `useMemo` mapping over the active networks. Because the parent passes a freshly
constructed `partialAccount` object each render, and the component is not memoized,
every parent re-render re-renders every indicator.

**Why it matters**

The CAIP account selector list eagerly mounts all rows (`initialNumToRender={999}`),
so on any list re-render (e.g. a selection toggle) every row's `AccountNetworkIndicator`
re-renders and re-subscribes/re-evaluates its selector. With 30+ accounts this is 30+
selector evaluations and avatar-group re-renders per interaction. `getActiveNetworksByScopes`
is a `createDeepEqualSelector` so its output stays referentially stable, meaning a
`React.memo` with a `partialAccount`-aware comparison would let unchanged rows bail out
of re-render entirely.

### Scenario

N/A — see Technical Details.

### Design

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

### Technical Details

**Evidence**

`app/components/UI/AccountNetworkIndicator/AccountNetworkIndicator.tsx:14`
```tsx
const AccountNetworkIndicator = ({ partialAccount }) => {
const networksWithTransactionActivity = useSelector((state) =>
getActiveNetworksByScopes(state, partialAccount),
);
...
};
export default AccountNetworkIndicator; // not memoized
```
Constructed per row at `app/components/UI/CaipAccountSelectorList/CaipAccountSelectorList.tsx:208`
(`partialAccount = { address, scopes }`).

**Fix**

Wrap in `React.memo` with a comparator on the meaningful fields:
```tsx
export default React.memo(
AccountNetworkIndicator,
(prev, next) =>
prev.partialAccount.address === next.partialAccount.address &&
prev.partialAccount.scopes === next.partialAccount.scopes,
);
```
Ideally also stabilize the `partialAccount` object in the parent row (e.g. once the row
is extracted into a memoized component) so identity-based memo works without a custom
comparator.

### Threat Modeling Framework

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

### Acceptance Criteria

- - `yarn jest app/components/UI/AccountNetworkIndicator`.
- Profile a selection toggle in the account selector with many accounts; indicator
re-render count per toggle should drop to only changed rows.

### References

- File: `app/components/UI/AccountNetworkIndicator/AccountNetworkIndicator.tsx:14`
- Source: MetaMask Mobile performance audit — finding `memo-account-network-indicator-not-memoized`
- Owner (CODEOWNERS / best-effort): @MetaMask/mobile-core-ux (suggested)
- Status: **UNVALIDATED**

Contributor guide

Open the contributing guide

Research direction

Start with app/components/UI/AccountNetworkIndicator/AccountNetworkIndicator.tsx and inspect its usage at app/components/UI/CaipAccountSelectorList/CaipAccountSelectorList.tsx:208. Run yarn jest app/components/UI/AccountNetworkIndicator, then profile a selection toggle with many accounts; done means unchanged account rows no longer re-render unnecessarily while the existing indicator behavior remains intact.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.