Skip to content

patch: adjusts reactivity calls within effects + primitive updates - #728

Merged
jer3m01 merged 7 commits into
solid2from
v2/reactivity-patches
Sep 7, 2026
Merged

jer3m01 merged 7 commits into
solid2from
v2/reactivity-patches

Conversation

@davedbase

Copy link
Copy Markdown
  1. 3d5f10b — fix: avoid STRICT_READ_UNTRACKED in more effect apply callbacks

The original 4 bugs found via live stack traces while diagnosing casebook-ui's console warnings:

  • toast-list.tsx — hotkey/pause-on-page-idle effects read ref() untracked
  • menu-trigger.tsx — menubar-focus effect read isNativeLink()/key()/triggerRef() untracked
  • create-dom-collection/utils.ts + create-dom-collection.ts — findDOMIndex, sortBasedOnDOMPosition, getCommonParent, and registerItem's cleanup read each item's .ref() untracked
  • tabs-root.tsx — selection-sync effect read isEmpty()/focusedKey()/isFocused()/setFocusedKey() untracked, plus a top-level selectedKey() read
  1. 51b5d73 — fix: audit and fix remaining STRICT_READ_UNTRACKED in effect apply callbacks

Full AST-based sweep of every createEffect(compute, apply) in the codebase, checking each apply-body accessor call against its actual type declaration. Found and fixed 16 more instances of the same bug: checkbox-input, collapsible-content (×2), combobox-base (×2), drawer-content, menu-sub-trigger, navigation-menu-content, navigation-menu-menu, number-field-root, radio-group-item-input, resizable-handle, segmented-control-indicator, hidden-select-base, select-base, text-field-text-area, time-field-segment, toast-progress-fill.

Also audited and correctly left alone ~10 other flagged sites — confirmed to be plain write-actions (not signal reads), stable non-reactive Map wrappers, or reads deferred into setTimeout/rAF/ResizeObserver callbacks that never run inside the effect's untracked window.

  1. 4dfd1f0 — chore: bump @solid-primitives/form and @solid-primitives/upload

Checked all 22 pinned @solid-primitives/* catalog entries against npm. 20 were already on the latest published prerelease; bumped form (next.2→next.3) and upload (next.3→next.4).

Verification on every commit: tsc --noEmit clean, full core suite (658 tests) passing, tsdown build clean.

Root cause, all 20 fixes: Solid 2.0's split createEffect(compute, apply) runs the apply phase untracked by design — any signal/context-accessor read there (even nested in a returned cleanup closure) trips the dev-mode STRICT_READ_UNTRACKED diagnostic. Fix is always untrack(), which changes no behavior — just makes the one-time-read intent explicit.

Four spots read reactive signals untracked inside a split-effect's
apply phase (which runs untracked by design in Solid 2.0), triggering
the dev-mode diagnostic on every mount:

- toast-list.tsx: hotkey and pause-on-page-idle effects read `ref()`
  directly, including inside a nested keydown closure.
- menu-trigger.tsx: the menubar-focus effect read `isNativeLink()`,
  `key()`, and `context.triggerRef()` directly.
- create-dom-collection/utils.ts: `findDOMIndex`, `sortBasedOnDOMPosition`,
  and `getCommonParent` read each item's `.ref()` accessor directly;
  these run from registerItem's effect-apply-phase call site.
- create-dom-collection.ts: registerItem's cleanup closure read
  `.ref()` on both items directly.
- tabs-root.tsx: the selection-sync effect read `isEmpty()`,
  `focusedKey()`, `isFocused()`, and called `setFocusedKey()` directly
  in its apply phase, plus a top-level `listState.selectedKey()` read
  outside any tracking scope.

All are one-time snapshot reads by design, so wrapping them in
untrack() changes no behavior and only makes the intent explicit.
…llbacks

A full codebase sweep (AST-based: every two-arg createEffect(compute, apply)
whose apply body calls a signal/context accessor directly) turned up 16 more
files with the same class of bug already fixed in toast-list, menu-trigger,
create-dom-collection, and tabs-root: reading a signal untracked inside the
split-effect apply phase, which runs untracked by design in Solid 2.0.

Fixed: checkbox-input, collapsible-content (x2 effects), combobox-base (x2),
drawer-content (ResizeObserver measure), menu-sub-trigger (cleanup),
navigation-menu-content, navigation-menu-menu, number-field-root,
radio-group-item-input, resizable-handle, segmented-control-indicator,
hidden-select-base, select-base, text-field-text-area, time-field-segment,
toast-progress-fill.

Also audited and deliberately left alone (verified not reactive, or reads
are deferred to a later async callback outside the effect's untracked apply
execution — timers, rAF, ResizeObserver/MutationObserver callbacks — so no
diagnostic ever fires): dismissable-layer's assignPointerEventToLayers,
drawer-content's second effect (double-rAF deferred), menu.tsx's close/
parentUnregister (plain write actions / stable non-reactive object),
navigation-menu-arrow (setTimeout-deferred), number-field-root's
context.format, resizable-handle's onDragEnd (deferred handler),
resizable-root's measure (no signal reads inside), tabs-content/tabs-trigger's
contentIdsMap/triggerIdsMap (plain Map wrapped in a stable non-reactive
accessor, not a real signal), toast-root's pauseTimer/resumeTimer/close/
deleteToast (plain write actions).

All 658 core tests pass; typecheck and build clean.
Checked all 22 pinned @solid-primitives/* catalog entries against npm;
20 were already on the latest published prerelease. Bumped the two that
had newer versions available:
- @solid-primitives/form: 1.0.0-next.2 -> 1.0.0-next.3
- @solid-primitives/upload: 1.0.0-next.3 -> 1.0.0-next.4

Typecheck, full core test suite (658 tests), and build all clean.
@netlify

netlify Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for kobalte ready!

Name Link
🔨 Latest commit 4dfd1f0
🔍 Latest deploy log https://app.netlify.com/projects/kobalte/deploys/6a986c96bc74bc0008ba12a9
😎 Deploy Preview https://deploy-preview-728--kobalte.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@davedbase
davedbase changed the base branch from main to solid2 September 2, 2026 18:36
@socket-security

socket-security Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

@davedbase

Copy link
Copy Markdown
Author

This PR will eventually include PR for @solid-primitives/event-listener (solidjs-community/solid-primitives#1048). This is now released as next-4 which contains the adjustment.

Newly published after resolving an npm token/publishing-access issue
upstream. @solid-primitives/pagination (also just republished) is not
a kobalte dependency, so nothing to bump there.

Typecheck, full core test suite (658 tests), and build all clean.
@davedbase
davedbase marked this pull request as ready for review September 2, 2026 19:44
biome ci flagged 3 files touched by the STRICT_READ_UNTRACKED fixes
for line-wrap/import-grouping differences from its formatter output.
Ran `biome check --write` on just those files; no logic changes.

biome ci and the full core test suite (658 tests) both clean.
@davedbase
davedbase marked this pull request as draft September 2, 2026 20:00
- tsdown: 0.22.7 -> 0.23.0
- unplugin-solid: 1.0.0 -> 2.0.0
- vitest: 4.1.10 -> 5.0.0

vitest 5 requires jest-dom's vitest-specific matcher types, which moved to
a separate `@testing-library/jest-dom/vitest` entry point in jest-dom 7
(previously bundled into the package's root types). Also bumped:
- @testing-library/jest-dom: 6.9.1 -> 7.0.1
- @testing-library/dom: 9.3.4 -> 10.4.1 (jest-dom 7's peer requirement)
- root tsconfig.json's `types` array now points at the /vitest subpath

The babel-preset-solid override (pinned to work around unplugin-solid@1.0.0's
outdated babel-preset-solid dependency) is left in place - untested whether
unplugin-solid@2.0.0 still needs it.

Typecheck clean across all workspace packages, full core test suite
(658 tests) passes, and the package builds clean.
…-next.5

next.4 shipped with an unresolved pnpm workspace-catalog protocol string
(catalog:peer) left in its peerDependencies instead of a resolved semver
range, so it fails to install under npm/yarn with EUNSUPPORTEDPROTOCOL.
next.5 is a pure republish fixing that metadata, no functional changes.

@solid-primitives/pagination is still not a kobalte dependency (confirmed
again via a repo-wide grep), so nothing to bump there.

Typecheck, full core test suite (658 tests), and build all clean.
@davedbase
davedbase marked this pull request as ready for review September 5, 2026 14:43
@jer3m01

jer3m01 commented Sep 7, 2026

Copy link
Copy Markdown
Member

LGTM!

@jer3m01
jer3m01 merged commit d3f8b4d into solid2 Sep 7, 2026
5 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Kobalte - Solid 2 Sep 7, 2026
@jer3m01
jer3m01 deleted the v2/reactivity-patches branch September 7, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants