[2.0 / next] A keyed Show on latest() of a staged write mounts its child twice when the flush parks (regressed by #4393)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đá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
- 48/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
Hướng nghiên cứu
Start with the failing matrix rows in packages/signals/tests/boundary-not-born-held-3540.test.ts and the equivalent cases in packages/web/test/loading-on-keyed-boundary-3540.spec.tsx. Read the linked design notes D-2 and D-22 and the reader-seat rebuild plan in #4395 to understand the intended in-place park re-derive. Done means both cases produce mount counts [1, 2, 2] while preserving the stated latest() behavior.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
A keyed <Show when={latest(count)}> whose when reads a write that is staged in the current flush but not yet held mounts its child twice when that flush parks. The pass runs provisionally (reads latest(count) → new value, the keyed Show mounts the child for key 2), the flush parks on a pending async read, and the pass is re-derived once as a verdict reader (D-2). Because the provisional owner was voided before the re-run, the keyed comparison is against the last committed when, so the same key mounts a second child. Mount side effects run twice for one key change. This regressed with #4393 (which was itself correct for #4365) and is currently pinned at the wrong number.
Reproduction
Signals-level (packages/signals/tests/boundary-not-born-held-3540.test.ts, matrix rows static over latest / callback over latest); web-level equivalent in packages/web/test/loading-on-keyed-boundary-3540.spec.tsx (show-static-latest, show-callback-latest):
const [count, setCount] = createSignal(1);
const data = createMemo(async () => { const v = count(); await delay(1000); return v; });
let mounts = 0;
const L = () => { mounts++; return <Loading fallback="Loading A">{data()}</Loading>; };
<Show keyed when={latest(count)}><L /></Show>
<Loading fallback="Loading B">{data()}</Loading> // B holds the frame on data (A33)
- Render; advance 1000ms; flush →
mounts === 1. setCount(2); flush(); await microtask; flush()— the frame parks ondata; A showsLoading Aearly (correct).mountsis 3; expected 2.- Advance 1000ms; flush →
2|2|2;mountsstays at the mid-flight count.
Observed [1, 3, 3], expected [1, 2, 2]. The <L /> factory (and anything a real child would do on mount — effects, refs, subscriptions) runs twice for a single key change.
Why it is a bug
- D-2 says a provisional pass that read a staging not yet held is re-derived at the park. It says nothing about discarding the pass's output.
latest()serves the staging on both runs (D-22, 2026-10-28 paragraph: "Inside a flush,latest()of a staging not yet held is the frame's read like any other"), so the key is identical across the two runs. Same key → one child → one mount.- No rule licenses a mount that depends on whether a flush happened to park. It is a timing-dependent double invocation. A remount is only correct when the verdict run yields a different key — an ordinary keyed change.
History
- #3540 added a
!probingarm toprovisionalVerdict("a provisional pass re-derived at the seam ran everything downstream of it twice (#3540 web)"), which produced[1, 2, 2]. - #4393 removed that arm to fix #4365 — correctly:
latest()must not open a transaction for its verdict — and re-pinned both tests to[1, 3, 3]with a "not a bug" comment. The removal was right; the re-pin was not.
Intended fix
An in-place park re-derive: re-run the pass under the same owner, with keyed children compared against the provisional run rather than the last commit. Scheduled for the reader-seat rebuild S1 (#4395). Explicitly not: restoring the !probing arm, the stagedScreen transaction variant, or letting latest() open a transaction.
Interim
The companion PR flips both pins to it.fails asserting the target [1, 2, 2], so they turn green when fixed instead of guarding the wrong number. List under known bugs in the next RC notes.
- Ngôn ngữ chính
- TypeScript
- Star
- 36.2k
- Fork
- 1.1k
- Merge trung bình
- 10 giờ 10 phút
- Pull request đã merge (30 ngày)
- 505
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của solidjs/solid
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 22/100
Maintainer thường phản hồi trong vòng 1 ngày
-
What gets passed to the `when` prop of a `Show` still gets executed twice on SSRCó thể đã có người làm @brenelz đã nhận 4 ngày trước. Đang mở1.x
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 52/100
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
solidjs/solid#4447 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của solidjs/solid
Issue tương tự
-
needs-area-label
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Website: the hero tip's video play() has no catch, so a refused play is an unhandled rejectionĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
liberation-data/drivine#615 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
backend bug ready-for-agent
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
geolens-io/geolens#3639 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
radius-project/ai-extensions#1196 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 88/100