Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[2.0 rc.15 + next] For row lifecycle cleanup inherits the list computation and halts on a shared-state write

Đang mở
#4,165 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@ryansolid đang làm issue này rồi.

Từ ngày 20/10/2026.

  • #4168 của @ryansolid — đang mở

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
51/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
javascript, typescript
Lĩnh vực
frontend, testing

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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 next at 2172a37687e92dd91758476423f08cb4a12bcb0c: 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.

Ngôn ngữ chính
TypeScript
Star
36.1k
Fork
1.1k
Merge trung bình
11 giờ 37 phút
Pull request đã merge (30 ngày)
344

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của solidjs/solid

Tất cả issue của solidjs/solid

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.