Memoize QRHardwareContext provider value and stabilize inline setters

Open Beginner friendly
#31,286 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

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

Research direction

Start with app/components/Views/confirmations/context/qr-hardware-context/qr-hardware-context.tsx at the provider around line 85, then read qr-hardware-context.test.tsx. Verify the provider callbacks and value are stable across unrelated updates without changing behavior. Run the specified Jest test and profile the QR-signing flow to confirm consumers avoid unnecessary re-renders.

Written by the indexing model from the issue text.

Description

area-performance Sev3 size-S ta-triaged team-confirmations

Performance audit finding · Severity: Medium · Effort: Easy · Fix risk: Simple · Test safety net: Partial
Owner: @MetaMask/confirmations
File: app/components/Views/confirmations/context/qr-hardware-context/qr-hardware-context.tsx:85

What is this about?

QRHardwareContextProvider passes an inline object literal to the provider, and two of its fields are inline arrow functions created fresh every render (setRequestCompleted: () => setRequestCompleted(true) and setSigningConfirmed: () => setSigningConfirmed(true)). Both the object and those function refs change on every render.

Why it matters

The provider re-renders whenever useQRHardwareAwareness() (a Redux selector on state.qrKeyringScanner) emits, plus on each local state change (scannerVisible, signingConfirmed, isRequestCompleted). With 6 consumer files in the QR-signing confirmation flow, every such update re-renders all consumers because the context value identity is unstable.

Scenario

N/A — see Technical Details.

Design

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

Technical Details

Evidence

app/components/Views/confirmations/context/qr-hardware-context/qr-hardware-context.tsx:85

<QRHardwareContext.Provider
  value={{
    pendingScanRequest,
    cameraError,
    cancelQRScanRequestIfPresent,
    isSigningQRObject,
    needsCameraPermission: isSigningQRObject && !hasCameraPermission,
    scannerVisible,
    setRequestCompleted: () => setRequestCompleted(true),
    setScannerVisible,
    setSigningConfirmed: () => setSigningConfirmed(true),
    signingConfirmed,
  }}
>

cancelQRScanRequestIfPresent is already useCallback-stabilized; the two inline setters and the object wrapper are not.

Fix

Promote the inline setters to useCallback-wrapped functions and wrap the value in useMemo:

const markRequestCompleted = useCallback(() => setRequestCompleted(true), []);
const confirmSigning = useCallback(() => setSigningConfirmed(true), []);
const value = useMemo(
  () => ({ pendingScanRequest, cameraError, cancelQRScanRequestIfPresent, isSigningQRObject,
    needsCameraPermission: isSigningQRObject && !hasCameraPermission, scannerVisible,
    setRequestCompleted: markRequestCompleted, setScannerVisible,
    setSigningConfirmed: confirmSigning, signingConfirmed }),
  [pendingScanRequest, cameraError, cancelQRScanRequestIfPresent, isSigningQRObject,
   hasCameraPermission, scannerVisible, markRequestCompleted, confirmSigning, signingConfirmed],
);
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/Views/confirmations/context/qr-hardware-context/qr-hardware-context.test.tsx.
  • Profile the QR-signing flow and confirm consumers don't re-render on unrelated selector ticks.
References
  • File: app/components/Views/confirmations/context/qr-hardware-context/qr-hardware-context.tsx:85
  • Source: MetaMask Mobile performance audit — finding context-qr-hardware-inline-value
  • Owner (CODEOWNERS / best-effort): @MetaMask/confirmations
  • Status: UNVALIDATED
Dominant language
TypeScript
Stars
3k
Forks
1.7k
Avg merge
1d 14h
Merged PRs (30d)
653

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from MetaMask/metamask-mobile

All issues in MetaMask/metamask-mobile

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.