Skip to main content

Performance

This guide covers performance optimization strategies for the Privacy Boost React Native SDK. Unlike the web SDK there’s no WASM to lazy-load — the heavy lifting runs in native iOS/Android binaries linked into the app. The costs to manage are SDK init, JS↔native bridge traffic, balance refresh frequency, and re-renders.

SDK Lifecycle

One Instance Per JS Runtime

PrivacyBoost holds a JWT, identity keys, and an internal note cache. Construct it once in a module-level ref or a context provider — not inside a component that may unmount and remount.
Or via context, lifted above any screen that needs it:

Defer Init Until First Use

The first call into the native SDK pays one-time setup cost. If your app has flows that don’t touch the privacy pool (onboarding, splash, marketing), defer construction until the first gated screen — keep it off cold-start.

Bridge Traffic

Every method on the SDK is a bridge call. The cost is small but not free — for tight loops or per-render fetches it adds up.

Avoid Per-Render Fetches

Batch Where Possible

Don’t await Inside forEach

Balance Refresh

Centralize Through One Hook

Put refresh logic in a single hook so screens don’t each schedule their own polling:
The module-scoped inflight deduplicates concurrent refreshes.

Refresh on Foreground, Not on a Timer

Tying refresh to setInterval drains battery. Use AppState:

Re-Renders

Selector Subscriptions

If you’re using a global store (Zustand, Redux, Jotai), subscribe to the smallest slice:

Stable Keys in FlatList

For transaction history, key by txHash so the list reuses rows:

Memoize Heavy Rows

If a row component does meaningful work (formatting amounts, deriving labels), wrap it in React.memo:

Network

Parallelize Independent Reads

Page History — Don’t Fetch It All

Memory

Drop Off-Screen Pages

For long histories, store the visible window only and refetch on scroll-back instead of accumulating every page in state. FlatList’s removeClippedSubviews and windowSize reduce native view memory but won’t help your JS state.

Don’t Wrap Big Arrays in useState

Arrays of Transaction objects can be heavy. Prefer a normalized store keyed by txHash so component state holds only ids:

Operation Timing

Wrap SDK calls to observe timing during development:
In production, send timings to your APM (Sentry Performance, Datadog RUM, Firebase Performance) so slow operations surface as percentile histograms.

What the SDK Already Optimizes

Things you do not need to layer on top:
  • Note cache — the SDK caches unspent notes natively and only refetches new merkle tree leaves.
  • Merkle tree pruning — proven nodes are kept; the rest are pruned.
  • JWT reuseclearSession() keeps identity keys so the next authenticate() is fast.
  • Native execution — proofs run on the native side; the JS thread is not blocked while a shield is being generated.

Best Practices Summary

  1. One SDK instance per JS runtime — hold it in a module-level ref or a context provider.
  2. Defer SDK init until the first privacy-pool screen.
  3. Don’t fetch in render bodies — use useEffect with cleanup.
  4. Refresh balances on AppState === 'active' and after writes, not on a timer.
  5. Deduplicate concurrent refreshes with an inflight ref.
  6. Subscribe to the smallest slice of state your screen needs.
  7. Page transaction history; don’t fetch it all.
  8. Use clearSession() over logout() when re-auth is expected soon.

Next Steps