Repository navigation
fix(signals): a removed row's cleanup can write during teardown (#4165) - #4168
Conversation
…can write (#4165) Removing a keyed For row disposed it inside the list's pass, so its cleanups ran with the list computation as context. A setter there was rejected as an owned-scope write and halted reactivity in development. Cleanups (_disposal entries and the final effect-returned cleanup) now run through runWithOwner(null), untracked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
🦋 Changeset detectedLatest commit: c9745ac The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
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 |
Size (brotli, eager entry chunk)
|
Coverage Report for CI Build 39391318236Coverage remained the same at 77.555%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Merging this PR will degrade performance by 10.6%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ |
build + consume |
694.3 µs | 776.6 µs | -10.6% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing fix/for-row-cleanup-write-4165 (c9745ac) with next (71153bf)
Footnotes
-
5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Generated by Claude Code |
Running cleanups with no owner broke getOwner() and getContext inside them. Instead, the dev-only owned-scope write checks (setSignal, markRefresh, the store setter) stand down while disposalDepth is raised, so a cleanup's writes are allowed and production output is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
Generated by Claude Code |
Summary
Fixes #4165.
Before: removing a keyed
<For>row disposed it inside the list's pass, so the row's cleanups ran while the list computation was the current scope. A setter in anonCleanuporonSettled(() => () => ...)cleanup was rejected withREACTIVE_WRITE_IN_OWNED_SCOPE, and reactivity halted in development. The row stayed mounted and later list updates were ignored. The report hit this in a real app: deleting a row from its own open menu, where the menu's teardown publishes shared state.After: a cleanup's signal, store or
refreshwrite is allowed during teardown, the same way an effect callback's is. Cleanups still run under their owner, sogetOwner()and context lookups inside them work as before, and errors thrown from a cleanup still reach the nearest boundary.How: owner teardown already raises the dev-only
disposalDepthcounter around cleanups. The three owned-scope write checks (setSignal,markRefreshand the store setter guard) now also skip when it is raised. Everything is behind__DEV__, so production output is unchanged (0 B).An earlier version of this PR ran cleanups through
runWithOwner(null, ...). That fixed the write but madegetOwner()return null andgetContextthrow inside cleanups, so it was replaced.How did you test this change?
packages/signals/tests/for-row-cleanup-write-4165.test.ts: amapArrayrow cleanup writing a signal, a branch-memo cleanup writing a signal, and a row cleanup writing a store all fail onnextand pass here. A fourth test checks that a row cleanup still sees its owner and a provided context.packages/web/test/for-row-cleanup-write-4165.spec.tsxis the report's shape: a<For>row with anonSettledcleanup or anonCleanupthat calls a setter, then removing the row and appending another. It fails onnextand passes here.Erroredboundary (a<For>row and a memo branch): same result onnextand with this change.dist/prodhas no reference to the counter.https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
Generated by Claude Code