Skip to content

Implement flags persistence, reset on reload, and web parity & bugfixes#432

Open
efahk wants to merge 13 commits into
feature/add-feature-flags-supportfrom
efahk_native_persistence
Open

Implement flags persistence, reset on reload, and web parity & bugfixes#432
efahk wants to merge 13 commits into
feature/add-feature-flags-supportfrom
efahk_native_persistence

Conversation

@efahk

@efahk efahk commented Jun 25, 2026

Copy link
Copy Markdown

Introduces several changes to attain parity with other client feature flags SDKs

  • Upgrades Android & iOS versions for the native bridges.
  • Implements reset reloading flag for react native web ( android & ios) will just bridge to reset on that end with the package update
  • React Native Web parity with Javascript browser SDK, as it had multiple bugs and behavior that was not in line. E.x Runtime event support
  • Qa'd in web non-native, ios native, android native mode

@efahk efahk changed the title Implement flags persistence, reset on reload, and web bugfixes Implement flags persistence, reset on reload, and web parity & bugfixes Jun 25, 2026
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge; the race-condition fixes and persistence layer are well-guarded, and the native bridges are additive changes behind an updated SDK version.

The critical correctness fixes (promise identity guard, updateContext invalidation, areFlagsReady TTL check) are all present and well-tested. The new persistence layer round-trips correctly and isolates data per token and distinct_id. The remaining findings are style/consistency concerns that do not affect correctness.

javascript/mixpanel-flags-js.js deserves a second look on the getVariant()/getAllVariants() error-path inconsistency and the redundant staleness check.

Important Files Changed

Filename Overview
javascript/mixpanel-flags-js.js Core JS-fallback implementation: adds fetch promise identity guard to prevent stale writes, fixes updateContext() to invalidate in-flight fetches, and areFlagsReady() now checks TTL staleness. Minor: redundant double-staleness check in getVariant().
javascript/mixpanel-flag-persistence.js New persistence layer: serializes/deserializes flag variants with TTL and distinct_id scoping. Policies are cleanly separated. save/load round-trip is consistent.
javascript/mixpanel-flags.js Facade class updated: adds getAllVariants/Sync, removes areFlagsReadySync guard before native delegate calls, updateContext no longer throws in native mode. MixpanelFlagsJS always constructed on access even when flags not enabled.
index.js reset() now chains flags.reset() in the .then() callback so JS-mode flags are cleared only after native reset succeeds. Fire-and-forget pattern is intentional per prior review thread.
ios/MixpanelReactNative.swift Adds getAllVariants/Sync and updateFlagsContext bridge methods. updateFlagsContext calls flags.setContext() which always merges; replace semantics are not forwarded (documented limitation).
android/src/main/java/com/mixpanel/reactnative/MixpanelReactNativeModule.java Adds getAllVariants/Sync and updateFlagsContext bridge methods matching iOS. options.replace is not forwarded to setContext (same documented limitation as iOS).
MixpanelReactNative.podspec Bumps Mixpanel-swift dependency from 5.1.3 to 6.5.0 to pick up native flags persistence and reset support.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[getVariant called] --> B[await persistenceLoadedPromise]
    B --> C{Policy?}
    C -->|PERSISTENCE_UNTIL_NETWORK_SUCCESS and areFlagsReady| D[getVariantSync return]
    C -->|NETWORK_FIRST or NETWORK_ONLY| E{fetchPromise set?}
    E -->|Yes| F[await fetchPromise]
    E -->|No| G[return NOT_READY fallback]
    F -->|success| H[getVariantSync return]
    F -->|error| I{areFlagsReady?}
    I -->|Yes| H
    I -->|No| J[return BACKEND_ERROR fallback]

    subgraph fetchFlags
      K[_fetchStartTime = now] --> L[build request]
      L --> M[fetchPromise = myPromise]
      M --> N{this.fetchPromise === myPromise?}
      N -->|No - abandoned| O[discard result]
      N -->|Yes| P[write flags + persist]
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[getVariant called] --> B[await persistenceLoadedPromise]
    B --> C{Policy?}
    C -->|PERSISTENCE_UNTIL_NETWORK_SUCCESS and areFlagsReady| D[getVariantSync return]
    C -->|NETWORK_FIRST or NETWORK_ONLY| E{fetchPromise set?}
    E -->|Yes| F[await fetchPromise]
    E -->|No| G[return NOT_READY fallback]
    F -->|success| H[getVariantSync return]
    F -->|error| I{areFlagsReady?}
    I -->|Yes| H
    I -->|No| J[return BACKEND_ERROR fallback]

    subgraph fetchFlags
      K[_fetchStartTime = now] --> L[build request]
      L --> M[fetchPromise = myPromise]
      M --> N{this.fetchPromise === myPromise?}
      N -->|No - abandoned| O[discard result]
      N -->|Yes| P[write flags + persist]
    end
Loading

Reviews (8): Last reviewed commit: "Address pr comment" | Re-trigger Greptile

Comment thread javascript/mixpanel-flags-js.js
Comment thread android/src/main/java/com/mixpanel/reactnative/MixpanelReactNativeModule.java Outdated
Comment thread index.js
Comment thread javascript/mixpanel-flags-js.js
Comment thread javascript/mixpanel-flags-js.js Outdated
Comment thread javascript/mixpanel-flags-js.js
Comment thread __tests__/flags.test.js Outdated
@efahk efahk requested a review from tylerjroach July 2, 2026 16:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant