Skip to content

Raven Mobile: FrappeNativeProvider's inline tokenParams object causes a new Socket.IO connection to be opened on every re-render (never closed) - connection reliability worsens the longer the app stays open #2219

Description

@makethemostoflife

Summary

apps/mobile/lib/FrappeNativeProvider.tsx passes a brand-new tokenParams object literal into <FrappeProvider> on every render:

<FrappeProvider
    url={siteInfo?.url}
    tokenParams={{
        type: 'Bearer',
        useToken: true,
        token: getAccessToken,
    }}
    siteName={siteInfo?.sitename}
    ...
>

frappe-react-sdk's FrappeProvider builds its entire config - including the Socket.IO connection - inside a useMemo keyed on tokenParams by object reference (src/lib/index.tsx). Since this object is recreated on every render, the memo reruns and opens a brand-new SocketIO connection every time, with no cleanup of the previous connection. The abandoned connection is simply left running (its own reconnection/heartbeat timers keep going) rather than closed.

Why this compounds over time, not just at startup

FrappeNativeProvider is a plain function component with no React.memo, so it re-renders whenever its parent (app/[site_id]/_layout.tsx) re-renders - which happens routinely, e.g. via that layout's own useNetworkState() hook firing on ordinary mobile connectivity changes (WiFi signal strength, cell handoff, foreground/background). Each such re-render creates a fresh tokenParams object, which reruns the SDK's useMemo, which opens another orphaned socket. On a phone this isn't rare - it can happen repeatedly for the entire time the app stays open, matching real user reports of reliability getting steadily worse the longer a session is left open, not just an issue at launch.

Evidence from a real production deployment

Analyzing live nginx access logs of real Raven Mobile (iOS) traffic over ~9 hours / 90 sessions: every mobile Engine.IO session died within ~1 second via 400 session ID unknown, immediately followed by a new session - during app bootstrap specifically, 3 separate overlapping sessions were created within ~1.3 seconds. This matches exactly what the mechanism above predicts.

Why this doesn't show up in typical web usage

This only affects consumers who pass tokenParams (token-based auth) to FrappeProvider - which a mobile app must do, since it has no shared browser cookie jar. Most web usage relies on session cookies instead, never sets tokenParams, so it stays undefined (a stable reference) and this code path never fires. It's specifically a mobile-app-shaped bug in a library mostly exercised by web usage.

Suggested fix

Memoize tokenParams in FrappeNativeProvider.tsx, keyed on the actual stable values, instead of constructing it inline every render:

const tokenParams = useMemo(() => ({
    type: 'Bearer' as const,
    useToken: true,
    token: getAccessToken,
}), [getAccessToken])

and pass tokenParams={tokenParams} instead of the inline literal. getAccessToken is already stable (wrapped in useCallback(..., []) in _layout.tsx), so this fully stabilizes the reference across re-renders. Opening a PR with this exact change.

Environment

  • raven develop/main branch (confirmed present as of 2026-07-24)
  • frappe-react-sdk latest main (confirmed present)
  • Affects apps/mobile/lib/FrappeNativeProvider.tsx

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions