[2.0 rc.15 + next] For row lifecycle cleanup inherits the list computation and halts on a shared-state write
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 51/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- javascript, typescript
调研方向
Start with the cleanup disposal path in packages/signals/src/map.ts and the cleanup invocation in core/owner.ts; then run the linked conformance case 058 with TARGET=rc15 bun test ./findings/058-list-cleanup-write/repro.test.ts. Trace how the cleanup inherits its write context and compare the For and Show controls. Done means the For cleanup releases shared state without halting reactivity, while ordinary computation writes remain rejected.
由索引模型根据 Issue 内容生成。
描述
What happens
Removing a row from a keyed For runs that row's lifecycle cleanup under the list's owned computation. An ordinary setter from the cleanup throws REACTIVE_WRITE_IN_OWNED_SCOPE, followed by REACTIVITY_HALTED. The row remains mounted and subsequent list updates are ignored. The identical cleanup unmounted by Show succeeds.
This reproduces with the documented component lifecycle form, onSettled(() => () => setCount(n => n + 1)), and with low-level onCleanup. It uses no async source, router, external UI library, SSR or hydration.
Minimal shape
import { render } from '@solidjs/web';
import { createSignal, flush, For, onSettled } from 'solid-js';
const [rows, setRows] = createSignal([1, 2]);
const [count, setCount] = createSignal(0);
function Row() {
onSettled(() => () => setCount(n => n + 1));
return <i>row</i>;
}
render(() => <For each={rows()}>{() => <Row />}</For>, document.body);
flush();
setRows([2]);
flush(); // development: owned-write error, then reactivity halted
Expected: one row remains, the release count becomes 1, and a later append works. An internal control-flow computation should not become the lifecycle callback's write context.
Executable case and version evidence
Fixed conformance case 058, including source, desired-behavior assertions and Show/ownedWrite controls.
After its frozen install, run:
TARGET=rc15 bun test ./findings/058-list-cleanup-write/repro.test.ts
- Published core/web/signals/compiler
2.0.0-rc.15(4937f0a2bfddd45a65738c189075aff577530073): both For cleanup variants fail in development; Show and ownedWrite controls pass. - Source-built current
nextat2172a37687e92dd91758476423f08cb4a12bcb0c: the same development failure. - Current HEAD production: the disposal/control cases pass because the owned-write guard is development-only. This report does not claim a production crash.
- Real system Google Chrome; native compiler; plain public JSX with no type bypass.
Raw versioned receipts and logs.
Source boundary and candidate repair
packages/signals/src/map.ts disposes removed row owners inside the list recomputation. core/owner.ts calls registered cleanups and final effect-returned cleanups directly, inheriting that ambient context. The setter guard then treats the cleanup as a computation write.
A local source experiment runs just the actual cleanup callbacks through runWithOwner(null, callback), restoring the caller's context/tracking afterward. It does not relax the setter guard or add a disposal-depth exemption. The original browser repro becomes green; ordinary computation writes and a computation nested inside cleanup remain rejected. Using the same Node/Vitest 4.1.6 runner, the upstream signals suite is 323 files / 5387 passes / 3 expected failures / 2 skips before and after. This is a tested direction, not a claim that the repair is upstream-reviewed.
Dedupe and application impact
Searched open and closed issues in solid, solid-router and solid-start. #4089 is cleanup rejoining an already-landed transaction; #3601 is reentrant double cleanup. This case needs neither a transaction nor reentrant disposal.
The real application trigger was deleting a row from its own open menu: dismissable-layer teardown publishes shared state to still-mounted UI. It currently requires deferred writes or ownedWrite declarations in library internals merely to survive list removal.
- 主要语言
- TypeScript
- 星标
- 36.2k
- 派生
- 1.1k
- 平均合并
- 11 小时
- 30 天内合并 PR
- 343
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
solidjs/solid 的其他 Issue
-
`lazy()`: remounting an already-loaded component renders empty (or the Suspense fallback) since 1.9.14可能已有人在做 @itisrmk 于 7 天前认领。 未关闭1.x
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
维护者通常 1 天内回复
-
enhancement
难度 5/5 一周以上 新手友好度 35/100
维护者通常 1 天内回复
-
`createRoot`'s dispose function keeps `fn` alive, so `<For>`/`<Index>` rows retain removed list items可能已有人在做 @boombang 于 9 天前认领。 未关闭1.x
难度 4/5 1-2 天 新手友好度 45/100
维护者通常 1 天内回复
-
enhancement
难度 5/5 一周以上 新手友好度 35/100
维护者通常 1 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
awesome-dsh-plugin/awesome-dsh-plugin#9184 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 75/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 85/100
farion1231/cc-switch#8829 ·
维护者通常 1 天内回复
-
[core-client] Remove unused hand-maintained transport helpers after the definition-table cutover未关闭doc-audit enhancement
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 85/100