Skip to content

fix(signals): re-wake tracked readers after staged commits - #3293

Closed
javascript-unsafe wants to merge 1 commit into
solidjs:nextfrom
javascript-unsafe:fix/3291-tracked-effect
Closed

fix(signals): re-wake tracked readers after staged commits#3293
javascript-unsafe wants to merge 1 commit into
solidjs:nextfrom
javascript-unsafe:fix/3291-tracked-effect

Conversation

@javascript-unsafe

Copy link
Copy Markdown

Closes #3291

What was broken

A signal written by a ref callback was not observed by a createTrackedEffect when the element was rendered through <Dynamic> inside a <Portal>.

The write landed in the signal, but the tracked effect ran only once and kept observing the old value.

Root cause

The rc.6 Dynamic namespace fix made intrinsic elements lazy. In a Portal, that moves spread() and the ref write into the render-effect phase. The tracked effect is notified before the staged signal value is committed, but committed-visibility reads intentionally return the old value. The commit then updated the signal without waking the tracked subscriber again.

Fix

Reuse the existing missed-wake latch when a committed-visibility reader observes a staged signal value. When the signal commits, tracked subscribers are queued again so they observe the committed value.

Tests

  • Added a regression test covering createTrackedEffect + Dynamic + Portal + ref.
  • packages/web: npx vitest run test/portal-dynamic-ref-3291.spec.tsx
  • Relevant signals tests: createTrackedEffect.test.ts, effect-phase-writes.test.ts, and treeshake.test.ts

@changeset-bot

changeset-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e03137d

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

@javascript-unsafe
javascript-unsafe changed the base branch from main to next September 6, 2026 07:09
@javascript-unsafe
javascript-unsafe force-pushed the fix/3291-tracked-effect branch 2 times, most recently from af4be24 to e03137d Compare September 6, 2026 07:25
@codspeed-hq

codspeed-hq Bot commented Sep 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 136 untouched benchmarks


Comparing javascript-unsafe:fix/3291-tracked-effect (f043ae6) with next (b3c94be)

Open in CodSpeed

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 34019000424

Coverage remained the same at 71.814%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 998
Covered Lines: 764
Line Coverage: 76.55%
Relevant Branches: 783
Covered Branches: 515
Branch Coverage: 65.77%
Branches in Coverage %: Yes
Coverage Strength: 14.95 hits per line

💛 - Coveralls

@ryansolid

Copy link
Copy Markdown
Member

Thanks for digging into this one — your root-cause read was right: the tracked effect was woken by the write, ran in the same pass with committed visibility, and nothing re-notified it once the value landed.

We fixed it from the other side in b3c94be, which is now on next: tracked-effect wakes (and the first run) ride the heap like every other subscriber, and GlobalQueue._update hands the callback to the user queue when the heap reaches it. That orders the run after the commit structurally, so no latch on the signal is needed — and the tracked special case in enqueueSub goes away rather than growing. The Portal + Dynamic + ref shape is pinned in packages/web/test/dynamic.spec.tsx, and #3187's lazy creation was reverted in d2b50e9 so ref fires at creation again. createTrackedEffect is also now marked @deprecated (c6c415b).

Closing as superseded. Appreciate the reproduction and the writeup.

Claude via Cursor

@ryansolid ryansolid closed this Sep 6, 2026
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.0.0-rc.6: a signal written from a ref is never observed when the element is rendered via <Dynamic> inside a <Portal>

3 participants