fix(react): isolate loader data from activity context#736
fix(react): isolate loader data from activity context#736ENvironmentSet wants to merge 4 commits into
Conversation
🦋 Changeset detectedLatest commit: 63fd197 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughSummary by CodeRabbit
WalkthroughLoader results move from activity context into loader-plugin-owned maps and are exposed through a React context. Initialization, restoration, navigation preloading, promotion, and cleanup now use event- and activity-keyed runtime state. ChangesLoader data isolation
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant RouteNavigation
participant loaderPlugin
participant LoaderDataProvider
participant Activity
RouteNavigation->>loaderPlugin: Start route preload
loaderPlugin->>loaderPlugin: Pause actions
loaderPlugin->>loaderPlugin: Store resolved loader data
loaderPlugin->>loaderPlugin: Resume actions
loaderPlugin->>LoaderDataProvider: Provide loader promise
LoaderDataProvider->>Activity: Render activity with loader context
loaderPlugin->>loaderPlugin: Remove staged data on next microtask
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@stackflow/link
@stackflow/plugin-basic-ui
@stackflow/plugin-blocker
@stackflow/plugin-google-analytics-4
@stackflow/plugin-history-sync
@stackflow/plugin-lifecycle
@stackflow/plugin-renderer-basic
@stackflow/plugin-renderer-web
@stackflow/react-ui-core
@stackflow/react
commit: |
Deploying stackflow-demo with
|
| Latest commit: |
63fd197
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9a925eec.stackflow-demo.pages.dev |
| Branch Preview URL: | https://feature-fep-2613.stackflow-demo.pages.dev |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
stackflow-docs | 63fd197 | Commit Preview URL | Jul 24 2026, 09:49 AM |
3058ce7 to
87286ba
Compare
Problem
The built-in
loaderPluginin@stackflow/reactstores loader results (as Promises) in the sharedactivityContext.loaderData. SinceactivityContextis shared across all plugins and is included verbatim inStackSnapshotevents:loaderDatakey can collide with other plugins' or users' data and be silently overwritten depending on execution order.StackSnapshot.Solution
Loader runtime state now lives in plugin-owned storage, fully outside
activityContext:loaderPlugininstance owns private Maps keyed by the entering domain-event ID, isolating multiple Stackflow/SSR store instances and keeping separate loader generations whenreplace()reuses an activity ID (including a rendered generation plus a queued paused replacement).wrapActivityprovides the promise foractivity.enteredBy.idtouseLoaderDatathrough an internal React Context — no render-phase mutation, no effects.exit-donegenerations, retains queued paused generations, and removes staged data for prevented actions.loaderPluginno longer reads or writesactivityContext.loaderData; values placed there by users or other plugins pass through unchanged (and are no longer consumed byuseLoaderData).No
@stackflow/corepublic contract changed. Existing public surfaces are preserved:useLoaderData, SSRinitialLoaderData, core v3load(restore) path with loader re-execution and deferred resolution,pausedEventshandling, pause/resume loading behavior, and the lazyshouldRenderImmediatelypath.Verification
yarn typecheck(all workspaces),@stackflow/react/@stackflow/plugin-stack-persistence/ demo builds — PASS@stackflow/core(14 suites / 84 tests),@stackflow/plugin-history-sync(7 suites / 52 tests) — PASSuseLoaderDatarender, foreign-key collision isolation, Snapshot JSON safety, core v3 restore round-trip, SSRinitialLoaderData, plugin-stack-persistence save/load round-trip, paused same-ID replacement generations, pending loader + lazy pause/resume, independent stores reusing identical IDs, exit-done / prevented-action cleanup — all PASS. The harness rejects any Promise found in captured Snapshot records and confirms deliberately-planted foreignactivityContext.loaderDatavalues are preserved for core/persistence whileuseLoaderDatareturns only the plugin-owned value.Closes FEP-2613
🤖 Generated with Claude Code
https://claude.ai/code/session_01EYjxc3pkrpHin2PhoVQABq