Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

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

Chiusa
#4,165 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
51/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
javascript, typescript
Ambito
frontend, testing

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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.

Lingua principale
TypeScript
Stelle
36.1k
Fork
1.1k
Merge medio
11h 17m
PR unite (30g)
371

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di solidjs/solid

Tutte le issue di solidjs/solid

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.