Skip to content

fix(signals): an optimistic store's first flight declares no transaction - #3264

Merged
ryansolid merged 1 commit into
solidjs:nextfrom
brenelz:fix/optimistic-store-first-flight
Sep 4, 2026
Merged

fix(signals): an optimistic store's first flight declares no transaction#3264
ryansolid merged 1 commit into
solidjs:nextfrom
brenelz:fix/optimistic-store-first-flight

Conversation

@brenelz

@brenelz brenelz commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

<Loading fallback={...}> around a createOptimisticStore(fn, seed) never showed its fallback in client mode. The whole page stayed blank until the initial fetch landed — content outside the boundary included — while createStore(fn, seed) and createOptimistic(fn, seed) in the same spot showed the fallback. ssr: true masked it because the server streams the fallback and hydration is not a scheduled mount.

Root cause

#3146 gave optimistic-store truth flights an owned, declared transaction. declareFlight opened one for the uninitialized first flight too. render()'s root insert is scheduled and rides transitions, so the mount was stashed until the flight settled.

Fix

A first flight declares nothing, the same rule the loading window (#2933) already follows: nothing has committed yet, so there is no truth to keep on screen and no optimistic state to protect. Refetch flights declare exactly as before; bare optimistic writes during an in-flight refetch still ride the flight's transaction (#2951).

Tests

  • tests/store/flight-owned-transaction.test.ts: first flight shows the boundary fallback, refetch keeps content, plus a createStore control. A user effect is what reproduces the hold; a render effect is not stashed by the transition.
  • packages/web/test/loading.spec.tsx: the exact app shape through render(), asserting <h1>Poll</h1><div>Loading...</div> at mount and the list after landing.

Both fail without the fix. The existing #3146 tests still pass.

🤖 Generated with Claude Code

The flight-owned transaction (solidjs#3146) was declared for the uninitialized
first ask too, so every transition-riding consumer — render()'s scheduled
root insert included — was held until the initial fetch landed: the page
stayed blank (content outside the boundary included) and the Loading
boundary's fallback never showed, while createStore(fn, seed) and
createOptimistic(fn, seed) in the same spot showed it.

Nothing has committed on a first flight, so there is no truth to keep on
screen and no optimistic state to protect: it now declares nothing, like
the loading window (solidjs#2933). Refetch flights declare exactly as before, so
bare optimistic writes during an in-flight refetch still ride the flight's
transaction (solidjs#2951). Regressions in the solidjs#3146 suite (a user effect is what
reproduces the hold) and in the web Loading spec through render().

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0102a57

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@solidjs/signals Patch
test-integration Patch
@solidjs/web Patch
@solidjs/babel-plugin Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
solid-js Patch
@solidjs/universal Patch

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

@codspeed-hq

codspeed-hq Bot commented Sep 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 136 untouched benchmarks


Comparing brenelz:fix/optimistic-store-first-flight (0102a57) with next (439a2fe)

Open in CodSpeed

@ryansolid
ryansolid merged commit 8a65e5e into solidjs:next Sep 4, 2026
7 checks passed
ryansolid added a commit that referenced this pull request Sep 4, 2026
…ary warning

Store-shaped variants of the deferred-root-mount design (deliberate: async at
the root without a <Loading> boundary holds the mount until first truth and
fires the dev warning; with a boundary the fallback shows and nothing warns).
The memo-over-promise paths were pinned; the store paths were verified only by
hand until now. Covers an optimistic store's first flight (post-#3264 it rides
pending propagation alone) and a derived store, plus the boundary-catch case.

Note for posterity: while writing these, the optimistic cases appeared to
blank-hold through the boundary — a stale signals dist from before #3264;
web tests consume dist, not src. Fresh build, all shapes correct.

Co-authored-by: Cursor <cursoragent@cursor.com>
ryansolid added a commit that referenced this pull request Sep 4, 2026
…atch

#3264/#3265/#3266 (+ dev-only #3263) land ~35 B on always-retained store
paths, and #3262's handleAsync try/catch adds ~5 B to the core scenarios.
A golf pass was attempted and measured strictly worse in every direction
(helper extraction +29 B, full inline +70 B, has-trap arm merge -1/+7 B) —
the graph is at its brotli optimum post-#3270. Ratchet per convention with
audit notes: createStore 14.16 -> 14.20 KB, isPending/latest 10.04 -> 10.05,
store-family app 26.25 -> 26.27. No package source changes, no changeset.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

2 participants